1. Libraries and functions

1.1 Libraries

Load the required libraries.

library(tidyverse)
library(sf)
library(here)
library(readxl)
library(scales)
library(DT)
library(brms)
library(tidybayes)
library(patchwork)
library(marginaleffects)
library(ggrepel)
library(scico)
library(ggdensity)
library(ggpubr)
library(units)
library(glue)
library(ggh4x)

1.2 Helper functions

Functions that we will use throughout the script

#labeller for years
year_labels <- c(1950:1963)

#The Glasgow mass minuture chest X-ray campaign happened between 11th March and 12th April 1957
#Segment for graphs to match ACF period
acf_start <- decimal_date(ymd("1957-03-11"))
acf_end <- decimal_date(ymd("1957-04-12"))

Function for counterfactual plots



plot_counterfactual <- function(model_data, model, population_denominator, outcome, grouping_var=NULL, re_formula,...){
  
  #labeller for years
  year_labels <- c(1950:1964) #extra year for the extant of the x-axis

  #The Glasgow mass minuture chest X-ray campaign happened between 11th March and 12th April 1957
  #Segment for graphs to match ACF period
  acf_start <- decimal_date(ymd("1957-03-11"))
  acf_end <- decimal_date(ymd("1957-04-12"))

  summary <- {{model_data}} %>%
    select(year, year2, y_num, acf_period, {{population_denominator}}, {{outcome}}, {{grouping_var}}) %>%
    add_epred_draws({{model}}, re_formula={{re_formula}}) %>%
    group_by(year2, acf_period, {{grouping_var}}) %>%
    mean_qi() %>%
    mutate(.epred_inc = .epred/{{population_denominator}}*100000,
          .epred_inc.lower = .epred.lower/{{population_denominator}}*100000,
          .epred_inc.upper = .epred.upper/{{population_denominator}}*100000) %>%
    mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                  acf_period=="c. post-acf" ~ "Post Intervention"))



  #create the counterfactual (no intervention), and summarise
  
  counterfact <-
    add_epred_draws(object = {{model}},
                    newdata = {{model_data}} %>%
                                  select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, {{outcome}}) %>%
                                  mutate(acf_period = "a. pre-acf"), re_formula={{re_formula}}) %>%
    group_by(year2, acf_period, {{grouping_var}}) %>%
    mean_qi() %>%
    mutate(.epred_inc = .epred/{{population_denominator}}*100000,
         .epred_inc.lower = .epred.lower/{{population_denominator}}*100000,
         .epred_inc.upper = .epred.upper/{{population_denominator}}*100000) %>%
    mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="c. post-acf" ~ "Post Intervention"))
  


  #plot the intervention effect
p <- summary %>%
    droplevels() %>%
    ggplot() +
    geom_vline(aes(xintercept=acf_start, linetype="Mass CXR screening intervention")) +
    geom_vline(aes(xintercept=acf_end, linetype="Mass CXR screening intervention")) +
    geom_ribbon(aes(ymin=.epred_inc.lower, ymax=.epred_inc.upper, x=year2, group = acf_period, fill=acf_period), alpha=0.5) +
    geom_ribbon(data = counterfact %>% filter(year>=1956), 
                aes(ymin=.epred_inc.lower, ymax=.epred_inc.upper, x=year2, fill="Counterfactual"), alpha=0.5) +
    geom_line(data = counterfact %>% filter(year>=1956), 
              aes(y=.epred_inc, x=year2, colour="Counterfactual")) +
    geom_line(aes(y=.epred_inc, x=year2, group=acf_period,  colour=acf_period)) +
    geom_point(data = {{model_data}} %>%
                 mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Pre-ACF",
                                               acf_period=="b. acf" ~ "ACF",
                                               acf_period=="c. post-acf" ~ "Post-ACF")), 
               aes(y={{outcome}}, x=year2, shape=fct_relevel(acf_period,
                                                             "Pre-ACF",
                                                             "ACF",
                                                             "Post-ACF")), size=2) +
    theme_grey() +
    scale_y_continuous(labels=comma, limits =c(0,400)) +
    scale_x_continuous(labels = year_labels,
                       breaks = year_labels) +
    scale_fill_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:", na.translate = F) +
    scale_colour_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:", na.translate = F) +
    scale_shape_discrete(name="Empirical data (period):", na.translate = F) +
    scale_linetype_manual(values = 2, name="") +
    labs(
      x = "",
      y = "CNR (per 100,000)"
    ) +
    guides(x = "axis_truncated", y = "axis_truncated") +
    theme(legend.position = "bottom",
          legend.box="vertical", 
          text = element_text(size=10),
          axis.text.x = element_text(size=10, angle = 90, hjust=1, vjust=0.5),
          legend.text = element_text(size=10),
          legend.spacing.y = unit(0.1, 'cm'),
          axis.line = element_line(colour = "black")) 

    facet_vars <- vars(...)

  if (length(facet_vars) != 0) {
    p <- p + facet_wrap(facet_vars)
  }
  p

}

Function for calculating measures of change over time (RR.peak, RR.level, RR.slope)


summarise_change <- function(model_data, model, population_denominator, grouping_var = NULL, re_formula = NULL) {
  
  #functions for calculating RR.peak
  #i.e. relative case notification rate in 1957 vs. counterfactual trend for 1957
  
  grouping_var <- enquo(grouping_var)
  
  if (!is.null({{grouping_var}})) {
    
    #make the prediction matrix, conditional on whether we want random effects included or not.
    out <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num == 8),
                    acf_period = c("a. pre-acf", "b. acf")
    )
  } else {
    
    out <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num == 8),
                    acf_period = c("a. pre-acf", "b. acf")
    )
  }
  
  peak_draws <- add_epred_draws(newdata = out,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
    mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
    group_by(.draw, !!grouping_var) %>%
    summarise(estimate = last(epred_cnr)/first(epred_cnr)) %>%
    ungroup() %>%
    mutate(measure = "RR.peak")
  
  peak_summary <- peak_draws %>%
    group_by(!!grouping_var) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.peak")
  
  
  #functions for calculating RR.level
  #i.e. relative case notification rate in 1958 vs. counterfactual trend for 1958
  
    if (!is.null({{grouping_var}})) {
    out2 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num == 9),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  } else {
    
    out2 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num == 9),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  }
  
    level_draws <- add_epred_draws(newdata = out2,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
    arrange(y_num, .draw) %>%
    mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
    group_by(.draw, !!grouping_var) %>%
    summarise(estimate = last(epred_cnr)/first(epred_cnr)) %>%
    ungroup() %>%
    mutate(measure = "RR.level")
  
  level_summary <- level_draws %>%
    group_by(!!grouping_var) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.level")
    
    
  #functions for calculating RR.slope
  #i.e. relative change in case notification rate in 1958-1963 vs. counterfactual trend for 1959-1963
  
    if (!is.null({{grouping_var}})) {
    out3 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  } else {
    
    out3 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  }
  
    slope_draws <- add_epred_draws(newdata = out3,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
        arrange(y_num) %>%
        ungroup() %>%
        mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
        group_by(.draw, y_num, !!grouping_var) %>%
        summarise(slope = last(epred_cnr)/first(epred_cnr)) %>%
        ungroup() %>%
        group_by(.draw, !!grouping_var) %>%
        summarise(estimate = last(slope)/first(slope)) %>%
        mutate(measure = "RR.slope")
  
  slope_summary <- slope_draws %>%
     group_by(!!grouping_var) %>%
      mean_qi(estimate) %>%
      mutate(measure = "RR.slope")
    
  #gather all the results into a named list
    lst(peak_draws=peak_draws, peak_summary=peak_summary, 
        level_draws=level_draws, level_summary=level_summary, 
        slope_draws=slope_draws, slope_summary=slope_summary)
  
}

Function for calculating difference from counterfactual


calculate_counterfactual <- function(model_data, model, population_denominator, grouping_var=NULL, re_formula=NA){
  
  #effect vs. counterfactual
  counterfact <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}) %>%
                                    mutate(acf_period = "a. pre-acf"),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, year, {{grouping_var}}, acf_period) %>%
      mutate(.epred_inc_counterf = .epred/{{population_denominator}}*100000, .epred_counterf=.epred)  %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, {{population_denominator}}, .draw, .epred_counterf, .epred_inc_counterf, {{grouping_var}})
  
  #Calcuate case notification rate per draw, then summarise.
  post_change <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, acf_period),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, year, {{grouping_var}}, acf_period) %>%
      mutate(.epred_inc = .epred/{{population_denominator}}*100000)  %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, {{population_denominator}}, {{grouping_var}}, .draw, .epred, .epred_inc, {{grouping_var}}) 
  
  #for the overall period
    counterfact_overall <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}) %>%
                                    mutate(acf_period = "a. pre-acf"),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, {{grouping_var}}) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select({{population_denominator}}, .draw, .epred, {{grouping_var}})  %>%
      group_by(.draw, {{grouping_var}}) %>%
      summarise(.epred_counterf = sum(.epred)) 
  
  #Calcuate case notification rate per draw, then summarise.
  post_change_overall <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, acf_period),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, {{grouping_var}}) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select({{population_denominator}}, {{grouping_var}}, .draw, .epred) %>%
      group_by(.draw, {{grouping_var}}) %>%
      summarise(.epred = sum(.epred)) 
  
  
counter_post <-
  left_join(counterfact, post_change) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf,
           diff_inc100k = .epred_inc - .epred_inc_counterf,
           rr_inc100k = .epred_inc/.epred_inc_counterf) %>%
    group_by(year, {{grouping_var}}) %>%
    mean_qi(cases_averted, pct_change, diff_inc100k, rr_inc100k) %>%
    ungroup()

counter_post_overall <-
  left_join(counterfact_overall, post_change_overall) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf) %>%
    group_by({{grouping_var}}) %>%
    mean_qi(cases_averted, pct_change) %>%
    ungroup()

lst(counter_post, counter_post_overall)

}

Function for tidying up counterfactuals (mostly for making nice tables)


tidy_counterfactuals <- function(data){
  data %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(year = as.character(year),
            cases_averted = glue::glue("{cases_averted} ({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change} ({pct_change.lower} to {pct_change.upper})"),
            diff_inc = glue::glue("{diff_inc100k} ({diff_inc100k.lower} to {diff_inc100k.upper})"),
            rr_inc = glue::glue("{rr_inc100k} ({rr_inc100k.lower} to {rr_inc100k.upper})"))
}


tidy_counterfactuals_overall <- function(data){
  data %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(year = as.character(year),
            cases_averted = glue::glue("{cases_averted} ({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change} ({pct_change.lower} to {pct_change.upper})"))
}

2. Data

Import datasets for analysis

2.1 Shapefiles

Make a map of Glasgow wards


glasgow_wards_1951 <- st_read(here("mapping/glasgow_wards_1951.geojson"))
Reading layer `glasgow_wards_1951' from data source 
  `/Users/petermacpherson/Documents/Documents - Peter’s Mac mini/Projects/Historical TB ACF 2023-11-28/Work/analysis/glasgow-cxr/mapping/glasgow_wards_1951.geojson' 
  using driver `GeoJSON'
Simple feature collection with 37 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -4.393502 ymin: 55.77464 xmax: -4.070411 ymax: 55.92814
Geodetic CRS:  WGS 84

#read in Scotland boundary
scotland <- st_read(here("mapping/Scotland_boundary/Scotland boundary.shp"))
Reading layer `Scotland boundary' from data source 
  `/Users/petermacpherson/Documents/Documents - Peter’s Mac mini/Projects/Historical TB ACF 2023-11-28/Work/analysis/glasgow-cxr/mapping/Scotland_boundary/Scotland boundary.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 1 feature and 1 field
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 5513 ymin: 530249 xmax: 470332 ymax: 1220302
Projected CRS: OSGB36 / British National Grid
#make a bounding box for Glasgow
bbox <- st_bbox(glasgow_wards_1951) |> st_as_sfc()

#plot scotland with a bounding box around the City of Glasgow
scotland_with_bbox <- ggplot() +
  geom_sf(data = scotland, fill="antiquewhite") +
  geom_sf(data = bbox, colour = "#C60C30", fill="antiquewhite") +
  theme_void() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA, linewidth = 0.5),
        panel.background = element_rect(fill = "#EAF7FA", size = 0.3))
Warning: The `size` argument of `element_rect()` is deprecated as of ggplot2 3.4.0.
Please use the `linewidth` argument instead.
#plot the wards
#note we tidy up some names to fit on map
glasgow_ward_map <- glasgow_wards_1951 %>%
  mutate(ward = case_when(ward=="Shettleston and Tollcross" ~ "Shettleston and\nTollcross",
                          ward=="Partick (West)" ~ "Partick\n(West)",
                          ward=="Partick (East)" ~ "Partick\n(East)",
                          ward=="North Kelvin" ~ "North\nKelvin",
                          ward=="Kinning Park" ~ "Kinning\nPark",
                          TRUE ~ ward)) %>%
  
  ggplot() +
  geom_sf(aes(fill=division)) +
  geom_sf_label(aes(label = ward), size=3, fill=NA, label.size = NA, colour="black") +
  #scale_colour_identity() +
  scale_fill_brewer(palette = "Set3", name="City of Glasgow Division") +
  theme_grey() +
  labs(x="",
       y="",
       fill="Division") +
  theme(legend.position = "top",
        
        panel.border = element_rect(colour = "grey78", fill=NA, linewidth = 0.5),
        panel.background = element_rect(fill = "antiquewhite", size = 0.3),
        panel.grid.major = element_line(color = "grey78")) +
  guides(fill=guide_legend(title.position = "top", title.hjust = 0.5, title.theme = element_text(face="bold")))

#add the map of scotland as an inset
glasgow_ward_map + inset_element(scotland_with_bbox, 0.75, 0, 1, 0.4)

ggsave(here("figures/s1.tiff"), height=10, width = 12)

NA
NA

3. Denominators

Load in the datasets for denonomiators, and check for consistency.


overall_pops <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "overall_population")

overall_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()

#shift year to midpoint
overall_pops <- overall_pops %>%
  mutate(year2 = year+0.5)

Note, we have three population estimates:

  1. Population without institutionalised people or people in shipping
  2. Population in institutions
  3. Population in shipping

(Population in shipping is estimated from the 1951 census, so is the same for most years)

3.1 Overall population

First, plot the total population


overall_pops %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2), alpha=0.5, colour = "mediumseagreen", fill="mediumseagreen") +
  geom_point(aes(y=total_population, x=year2), colour = "mediumseagreen") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(
    title = "Glasgow Corporation: total population",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist()

NA
NA

Now the population excluding institutionalised and shipping population


overall_pops %>%
  ggplot() +
  geom_area(aes(y=population_without_inst_ship, x=year2), alpha=0.5, colour = "purple", fill="purple") +
  geom_point(aes(y=population_without_inst_ship, x=year2), colour = "purple") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(
    title = "Glasgow Corporation: population excluding institutionalised and shipping",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist()

NA
NA

3.2 Population by Ward

There are 5 Divisions containing 37 Wards in the Glasgow Corporation, with consistent boundaries over time.

#look-up table for divisions and wards
ward_lookup <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "divisions_wards")


ward_pops <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "ward_population")

ward_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()

#shift year to midpoint
ward_pops <- ward_pops %>%
  mutate(year2 = year+0.5)

#Get the Division population
division_pops <- ward_pops %>%
  group_by(division, year) %>%
  summarise(population_without_inst_ship = sum(population_without_inst_ship, na.rm = TRUE),
            institutions = sum(institutions, na.rm = TRUE),
            shipping = sum(shipping, na.rm = TRUE),
            total_population = sum(total_population, na.rm = TRUE))
`summarise()` has grouped output by 'division'. You can override using the `.groups` argument.
division_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA

Plot the overall population by Division and Ward


division_pops %>%
  mutate(year2 = year+0.5) %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2, colour=division, fill=division), alpha=0.8) +
  geom_point(aes(y=total_population, x=year2, colour=division)) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  facet_wrap(division~.) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_brewer(palette = "Set3", name = "") +
  scale_colour_brewer(palette = "Set3", name = "") +
  labs(
    title = "Glasgow Corporation: total population by Division",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA

ward_pops %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2, colour=division, fill=division)) +
  geom_point(aes(y=total_population, x=year2, colour=division), colour="black") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  facet_wrap(ward~., ncol=6) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_brewer(palette = "Set3", name="Division") +
  scale_colour_brewer(palette = "Set3", name = "Division") +
  labs(
    x = "",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

ggsave(here("figures/s3.tiff"), height=14, width=12)

Approximately, how many person-years of follow-up do we have?


overall_pops %>%
  ungroup() %>%
  summarise(across(year, length, .names = "years"),
            across(c(population_without_inst_ship, total_population), sum)) %>%
  mutate(across(where(is.double), comma)) %>%
  datatable()
NA
NA

Change in population by ward


ward_pops %>%
  group_by(ward) %>%
  summarise(pct_change_pop = (last(population_without_inst_ship) - first(population_without_inst_ship))/first(population_without_inst_ship)) %>%
  mutate(pct_change_pop = percent(pct_change_pop)) %>%
  arrange(pct_change_pop) %>%
  datatable()
NA
NA
NA

3.3 Population by age and sex


age_sex <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "age_sex_population") %>%
  pivot_longer(cols = c(male, female),
               names_to = "sex")

#collapse down to smaller age groups to be manageable
age_sex <- age_sex %>%
  ungroup() %>%
  mutate(age = case_when(age == "0 to 4" ~ "00 to 04",
                         age == "5 to 9" ~ "05 to 14",
                         age == "10 to 14" ~ "05 to 14",
                         age == "15 to 19" ~ "15 to 24",
                         age == "20 to 24" ~ "15 to 24",
                         age == "25 to 29" ~ "25 to 34",
                         age == "30 to 34" ~ "25 to 34",
                         age == "35 to 39" ~ "35 to 44",
                         age == "40 to 44" ~ "35 to 44",
                         age == "45 to 49" ~ "45 to 59",
                         age == "50 to 54" ~ "45 to 59",
                         age == "55 to 59" ~ "45 to 59",
                         TRUE ~ "60 & up")) %>%
  group_by(year, age, sex) %>%
  mutate(value = sum(value)) %>%
  ungroup()



m_age_sex <- lm(value ~ splines::ns(year, knots = 3)*age*sex, data = age_sex)

summary(m_age_sex)
Warning: essentially perfect fit: summary may be unreliable

Call:
lm(formula = value ~ splines::ns(year, knots = 3) * age * sex, 
    data = age_sex)

Residuals:
       Min         1Q     Median         3Q        Max 
-2.107e-10 -7.560e-13  0.000e+00  0.000e+00  2.107e-10 

Coefficients: (14 not defined because of singularities)
                                                    Estimate Std. Error    t value Pr(>|t|)    
(Intercept)                                        5.222e+04  3.820e-10  1.367e+14   <2e-16 ***
splines::ns(year, knots = 3)1                     -8.043e+03  7.621e-10 -1.055e+13   <2e-16 ***
splines::ns(year, knots = 3)2                             NA         NA         NA       NA    
age05 to 14                                        3.669e+04  4.679e-10  7.843e+13   <2e-16 ***
age15 to 24                                       -3.893e+03  4.679e-10 -8.320e+12   <2e-16 ***
age25 to 34                                       -3.996e+04  4.679e-10 -8.540e+13   <2e-16 ***
age35 to 44                                       -4.230e+04  4.679e-10 -9.040e+13   <2e-16 ***
age45 to 59                                        5.459e+04  4.411e-10  1.238e+14   <2e-16 ***
age60 & up                                         7.533e+04  4.126e-10  1.826e+14   <2e-16 ***
sexmale                                            3.374e+03  5.402e-10  6.244e+12   <2e-16 ***
splines::ns(year, knots = 3)1:age05 to 14         -1.863e+03  9.334e-10 -1.996e+12   <2e-16 ***
splines::ns(year, knots = 3)2:age05 to 14                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age15 to 24          7.533e+04  9.334e-10  8.070e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age15 to 24                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age25 to 34          1.325e+05  9.334e-10  1.420e+14   <2e-16 ***
splines::ns(year, knots = 3)2:age25 to 34                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age35 to 44          1.380e+05  9.334e-10  1.479e+14   <2e-16 ***
splines::ns(year, knots = 3)2:age35 to 44                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age45 to 59          3.474e+03  8.800e-10  3.948e+12   <2e-16 ***
splines::ns(year, knots = 3)2:age45 to 59                 NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age60 & up          -8.453e+04  8.232e-10 -1.027e+14   <2e-16 ***
splines::ns(year, knots = 3)2:age60 & up                  NA         NA         NA       NA    
splines::ns(year, knots = 3)1:sexmale             -1.994e+03  1.078e-09 -1.850e+12   <2e-16 ***
splines::ns(year, knots = 3)2:sexmale                     NA         NA         NA       NA    
age05 to 14:sexmale                                1.053e+04  6.617e-10  1.592e+13   <2e-16 ***
age15 to 24:sexmale                                2.352e+04  6.617e-10  3.555e+13   <2e-16 ***
age25 to 34:sexmale                                1.355e+04  6.617e-10  2.047e+13   <2e-16 ***
age35 to 44:sexmale                               -1.727e+03  6.617e-10 -2.611e+12   <2e-16 ***
age45 to 59:sexmale                                2.774e+03  6.238e-10  4.446e+12   <2e-16 ***
age60 & up:sexmale                                -7.761e+04  5.835e-10 -1.330e+14   <2e-16 ***
splines::ns(year, knots = 3)1:age05 to 14:sexmale -2.049e+04  1.320e-09 -1.552e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age05 to 14:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age15 to 24:sexmale -6.780e+04  1.320e-09 -5.136e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age15 to 24:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age25 to 34:sexmale -3.804e+04  1.320e-09 -2.882e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age25 to 34:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age35 to 44:sexmale -1.171e+04  1.320e-09 -8.874e+12   <2e-16 ***
splines::ns(year, knots = 3)2:age35 to 44:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age45 to 59:sexmale -3.473e+04  1.244e-09 -2.791e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age45 to 59:sexmale         NA         NA         NA       NA    
splines::ns(year, knots = 3)1:age60 & up:sexmale   1.056e+05  1.164e-09  9.071e+13   <2e-16 ***
splines::ns(year, knots = 3)2:age60 & up:sexmale          NA         NA         NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.755e-11 on 44 degrees of freedom
Multiple R-squared:      1, Adjusted R-squared:      1 
F-statistic: 1.714e+29 on 27 and 44 DF,  p-value: < 2.2e-16
age_levels <- age_sex %>% select(age) %>% distinct() %>% pull() 

age_sex_nd <- 
  crossing(
    age=age_levels,
    sex=c("male", "female"),
    year = 1950:1963
  )

pred_pops <- age_sex_nd %>% modelr::add_predictions(m_age_sex)
Warning: prediction from rank-deficient fit; attr(*, "non-estim") has doubtful cases
pred_pops %>%
  ggplot(aes(x=year, y=pred, colour=age)) +
  geom_line() +
  geom_point() +
  facet_grid(sex~.) +
  scale_y_continuous(labels = comma, limits = c(0, 125000))


#How well do they match up with our overall populations?
pred_pops %>%
  group_by(year) %>%
  summarise(sum_pred_pop = sum(pred)) %>%
  right_join(overall_pops) %>%
  select(year, sum_pred_pop, population_without_inst_ship, total_population) %>%
  pivot_longer(cols = c(sum_pred_pop, population_without_inst_ship, total_population)) %>%
  ggplot(aes(x=year, y=value, colour=name)) +
  geom_point() +
  scale_y_continuous(labels = comma, limits = c(800000, 1250000))
Joining with `by = join_by(year)`

pred_pops %>%
  group_by(year, sex) %>%
  summarise(sum = sum(pred)) %>%
  group_by(year) %>%
  mutate(sex_ratio = first(sum)/last(sum))
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.

Population pyramids


label_abs <- function(x) {
  comma(abs(x))
}


pred_pops %>%
  ungroup() %>%
  group_by(year) %>%
  mutate(year_pop = sum(pred),
         age_sex_pct = percent(pred/year_pop, accuracy=0.1)) %>%
  mutate(sex = case_when(sex=="male" ~ "Male",
                         sex=="female" ~ "Female")) %>%
  ggplot(
    aes(x = age, fill = sex, 
        y = ifelse(test = sex == "Female",yes = -pred, no = pred))) + 
  geom_bar(stat = "identity") +
  geom_text(aes(label = age_sex_pct),
            position= position_stack(vjust=0.5), colour="black", size=2.5) +
  facet_wrap(year~., ncol=7) +
  coord_flip() +
  scale_y_continuous(labels = label_abs) +
  scale_fill_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  theme_ggdist() +
  theme(axis.text.x = element_text(angle=90, hjust = 1, vjust=0.5),
        legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="", y="") 


ggsave(here("figures/s4.tiff"), width=10)
Saving 10 x 4.51 in image

Not perfect, but resonably good. But ahhhhh… the age groups don’t align with the case notification age groups! Come back to think about this later.

4. Tuberculosis cases

Import the tuberculosis cases dataset

4.1 Overall notifications

Overall, by year.


cases_by_year <- read_xlsx("2023-11-28_glasgow-acf.xlsx", sheet = "by_year")

cases_by_year%>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()


#shift year to midpoint
cases_by_year <- cases_by_year %>%
  mutate(year2 = year+0.5)

Plot the overall number of case notified per year, by pulmonary and extra pulmonary classification.


cases_by_year %>%
  select(-total_notifications, -year) %>%
  pivot_longer(cols = c(pulmonary_notifications, `non-pulmonary_notifications`)) %>%
  mutate(name = case_when(name == "pulmonary_notifications" ~ "Pulmonary TB",
                          name == "non-pulmonary_notifications" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=value, x=year2, group = name, fill=name), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA

4.2 Notifications by Division

Read in the datasets and merge together.


#list all the sheets
all_sheets <- excel_sheets("2023-11-28_glasgow-acf.xlsx")

#get the ward sheets
ward_sheets <- enframe(all_sheets) %>%
  filter(grepl("by_ward", value)) %>%
  pull(value)


cases_by_ward_sex_year <- map_df(ward_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

cases_by_ward_sex_year %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA

Aggregate together to get cases by division


cases_by_division <- cases_by_ward_sex_year %>%
  left_join(ward_lookup) %>%
  group_by(division, year, tb_type) %>%
  summarise(cases = sum(cases, na.rm = TRUE))
Joining with `by = join_by(ward)``summarise()` has grouped output by 'division', 'year'. You can override using the `.groups` argument.
#shift year to midpoint
cases_by_division <- cases_by_division %>%
  mutate(year2 = year+0.5) %>%
  ungroup()

cases_by_division  %>%
  select(-year2) %>%
  select(year, everything()) %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()


cases_by_division %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=cases, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(division~., scales = "free_y") +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications by Division",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

4.3 Notifications by ward



cases_by_ward <- cases_by_ward_sex_year %>%
  group_by(ward, year, tb_type) %>%
  summarise(cases = sum(cases, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'ward', 'year'. You can override using the `.groups` argument.
cases_by_ward %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  select(year, everything()) %>%
  datatable()

#shift year to midpoint
cases_by_ward <- cases_by_ward %>%
  mutate(year2 = year+0.5)

cases_by_ward %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=cases, x=year2, group = tb_type, fill=tb_type), alpha=0.8) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(ward~., scales = "free_y") +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications by Ward",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme(legend.position = "bottom")

NA
NA

4.4 Notifications by age and sex

As we don’t have denominators, we will just model the change in counts.


#list all the sheets
all_sheets <- excel_sheets("2023-11-28_glasgow-acf.xlsx")

#get the ward sheets
age_sex_sheets <- enframe(all_sheets) %>%
  filter(grepl("by_age_sex", value)) %>%
  pull(value)


cases_by_age_sex <- map_df(age_sex_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

cases_by_age_sex %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA
NA

4.5 Uptake of screening

What percentage of adults (15+ participated in the intervention in 1957)?

Note that in the Report of Sir Kenneth Cowan, we have the following estimates of participation (we will use these for the manuscript, as they are not based on my estimates)

male_adult_resident_participation <- 281875
female_adult_resident_participation <- 340474
male_adult_resident_population <- 381713
female_adult_resident_population <- 437588

#overall participation
(male_adult_resident_participation+female_adult_resident_participation)/(male_adult_resident_population+female_adult_resident_population)
[1] 0.7596097
#male participation
male_adult_resident_participation/male_adult_resident_population
[1] 0.7384475
#female participation
female_adult_resident_participation/female_adult_resident_population
[1] 0.7780698

Look at uptake of screening by age and sex



uptake_age_sex <- read_xlsx("2024-03-26_mass_xray_uptake.xlsx", sheet = "uptake_age_sex")

uptake_graph <- uptake_age_sex %>%
  mutate(uptake = examined/resident_population) %>%
  mutate(examined_l = comma(examined),
         resident_population_l = comma(resident_population),
         uptake_l = percent(uptake, accuracy=0.1)) %>%
  mutate(label = glue("{examined_l}/{resident_population_l} ({uptake_l})")) %>%
  filter(age !="00-14") %>%
  mutate(sex = case_when(sex=="m" ~ "Male",
                         sex=="f" ~ "Female")) %>%
  ggplot(aes(x=age, y=uptake, group=sex, fill=sex)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label=uptake_l), position = position_dodge(width=0.85),vjust=2) +
  scale_y_continuous(labels=percent) +
  scale_fill_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="", y="")

Combine figure with table for single figure.


uptake_table <- uptake_age_sex %>%
  mutate(resident_population = comma(resident_population),
         examined = comma(examined)) %>%
  rename(Sex = sex,
         Age = age,
         `Resident population` = resident_population,
         Examined = examined) %>%
  mutate(Sex = case_when(Sex=="m" ~ "Male",
                         Sex=="f" ~ "Female"))

uptake_graph / gridExtra::tableGrob(uptake_table, rows = NULL)

ggsave(here("figures/s5.tiff"), height=10)
Saving 7.29 x 10 in image

Uptake by division


uptake_division <- read_xlsx("2024-03-26_mass_xray_uptake.xlsx", sheet = "uptake_division")

division_pops %>%
  filter(year==1957) %>%
  select(division, population_without_inst_ship) %>%
  left_join(uptake_division) %>%
  mutate(pct_pop_examined = examined/population_without_inst_ship)
Joining with `by = join_by(division)`

5 TB case notification rates

5.1 Overall TB case notification rates

Now calculate case notification rates per 100,000 population

Merge the notification and population denominator datasets together.

Here we need to include the whole population (with shipping and institutions) as they are included in the notifications.


overall_inc <- overall_pops %>%
  left_join(cases_by_year)
Joining with `by = join_by(year, year2)`
overall_inc <- overall_inc %>%
  mutate(inc_pulm_100k = pulmonary_notifications/total_population*100000,
         inc_ep_100k = `non-pulmonary_notifications`/total_population*100000,
         inc_100k = total_notifications/total_population*100000)

overall_inc %>%
  select(year, inc_100k, inc_pulm_100k, inc_ep_100k) %>%
  mutate_at(.vars = vars(inc_100k, inc_pulm_100k, inc_ep_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
Please use a list of either functions or lambdas: 

  # Simple named list: 
  list(mean = mean, median = median)

  # Auto named with `tibble::lst()`: 
  tibble::lst(mean, median)

  # Using lambdas
  list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))

overall_inc %>%
  select(year2, inc_pulm_100k, inc_ep_100k) %>%
  pivot_longer(cols = c(inc_pulm_100k, `inc_ep_100k`)) %>%
  mutate(name = case_when(name == "inc_pulm_100k" ~ "Pulmonary TB",
                          name == "inc_ep_100k" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=value, x=year2, group = name, fill=name), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Case notification rate (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA
NA

Change in case notification rates pre-intervention

#pre-ACF
overall_inc %>%
  filter(year %in% 1950:1956) %>%
  summarise(change = (((last(inc_pulm_100k)-first(inc_pulm_100k))/first(inc_pulm_100k))/7)*100)

#post-ACF
overall_inc %>%
  filter(year %in% 1958:1963) %>%
  summarise(change = (((last(inc_pulm_100k)-first(inc_pulm_100k))/first(inc_pulm_100k))/6)*100)
NA

5.2 TB case notification rates by Division


division_inc <- division_pops %>%
  left_join(cases_by_division)
Joining with `by = join_by(division, year)`
division_inc <- division_inc %>%
  mutate(inc_100k = cases/total_population*100000)

division_inc %>%
  select(year, division, tb_type, inc_100k) %>%
  mutate_at(.vars = vars(inc_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
Please use a list of either functions or lambdas: 

  # Simple named list: 
  list(mean = mean, median = median)

  # Auto named with `tibble::lst()`: 
  tibble::lst(mean, median)

  # Using lambdas
  list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))

division_inc %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=inc_100k, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(division~.) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate, by Division",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Case notification rate (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA
NA

5.2 TB case notification rates by Ward

Here we will filter out the institutions and harbour from the denominators, as we don’t have reliable population denominators for them.


ward_inc <- ward_pops %>%
  left_join(cases_by_ward)
Joining with `by = join_by(ward, year, year2)`
ward_inc <- ward_inc %>%
  mutate(inc_100k = cases/population_without_inst_ship*100000)

ward_inc %>%
  select(year, ward, tb_type, inc_100k) %>%
  mutate_at(.vars = vars(inc_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
Please use a list of either functions or lambdas: 

  # Simple named list: 
  list(mean = mean, median = median)

  # Auto named with `tibble::lst()`: 
  tibble::lst(mean, median)

  # Using lambdas
  list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))

ward_inc %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=inc_100k, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(ward~.) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate, by Ward",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Incidence (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme(legend.position = "bottom")

NA
NA
NA
NA

On a map


st_as_sf(left_join(ward_inc, glasgow_wards_1951)) %>%
  filter(tb_type=="Pulmonary") %>%
  ggplot() +
  geom_sf(aes(fill=inc_100k)) +
  facet_wrap(year~., ncol = 7) +
  scale_fill_viridis_c(name="Case notification rate (per 100,000)",
                       option = "A") +
  theme_ggdist() +
  theme(legend.position = "top",
        legend.key.width = unit(2, "cm"),
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  guides(fill=guide_colorbar(title.position = "top"))
Joining with `by = join_by(division, ward, ward_number)`

6. TB Mortality

6.1 Overall Mortality

Import the TB mortality data.

First, overall deaths. Note that in the original reports, we have a pulmonary TB death rate per million for all years, and numbers of pulmonary TB deaths for each year apart from 1950.


#get the overall mortality sheets
deaths_sheets <- enframe(all_sheets) %>%
  filter(grepl("deaths", value)) %>%
  pull(value)


overall_deaths <- map_df(deaths_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

overall_deaths %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA
NA
NA

Plot the raw numbers of pulmonary deaths


overall_deaths %>%
  ggplot(aes(x=year, y=pulmonary_deaths)) +
  geom_line(colour = "#DE0D92") +
  geom_point(colour = "#DE0D92") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  labs(y="Pulmonary TB deaths per year",
       x = "Year",
       title = "Numbers of pulmonary TB deaths",
       subtitle = "Glasgow, 1950-1963",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: no data for 1950") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA))

NA
NA

Now the incidence of pulmonary TB death

overall_deaths %>%
  ggplot(aes(x=year, y=pulmonary_death_rate_per_100k)) +
  geom_line(colour = "#4D6CFA") +
  geom_point(colour = "#4D6CFA") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(y="Annual incidence of death (per 100,000)",
       x = "Year",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA))

ggsave(here("figures/deaths.tiff"), width=10)
Saving 10 x 4.51 in image

6. Table 1

Make Table 1 here, and save for publication.


overall_pops %>% 
  select(year, total_population) %>%
  left_join(overall_inc %>%
              select(year, 
                     pulmonary_notifications, inc_pulm_100k,
                     `non-pulmonary_notifications`, inc_ep_100k,
                     total_notifications, inc_100k)) %>%
  left_join(overall_deaths %>%
              select(year,
                     pulmonary_deaths, pulmonary_death_rate_per_100k)) %>%
  mutate(percent_pulmonary = percent(pulmonary_notifications/(total_notifications ), accuracy=0.1)) %>%
  mutate(across(where(is.numeric) & !(year),  ~round(., digits=1))) %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.)))
Joining with `by = join_by(year)`Joining with `by = join_by(year)`

Comparison fo age-sex distribution of cases in 1950-1956 vs. 1957


label_abs2 <- function(x) {
  percent(abs(x))
}



cases_by_age_sex %>% 
  ungroup() %>%
  filter(tb_type=="Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(acf_period, age, sex) %>%
  summarise(cases = sum(cases)) %>%
  ungroup() %>%
  group_by(acf_period) %>%
  mutate(period_total = sum(cases)) %>%
  mutate(pct = cases/period_total) %>%
  mutate(pct2 = case_when(sex=="F" ~ -pct,
                          TRUE ~ pct)) %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
                 mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Pre-ACF",
                                               acf_period=="b. acf" ~ "ACF",
                                               acf_period=="c. post-acf" ~ "Post-ACF")) %>%
  ggplot() +
  geom_vline(aes(xintercept=0), linetype=2) +
  geom_point(aes(x=pct2,y=age, colour=fct_relevel(acf_period,
                                                             "Pre-ACF",
                                                             "ACF",
                                                             "Post-ACF")), stat="identity") +
  scale_x_continuous(labels=label_abs2, limits = c(-0.2, 0.2)) +
  scale_colour_manual(values = c("#DE0D92", "grey50", "#4D6CFA")) +
  theme_grey(base_family = "Aptos") +
  labs(x= "<- Female                Percent of cases              Male ->",
       y="") +
  theme(legend.title = element_blank(),
        legend.position = "bottom")
`summarise()` has grouped output by 'acf_period', 'age'. You can override using the `.groups` argument.
ggsave(here("figures/s6.tiff"))
Saving 7.29 x 4.51 in image

Prepare the datasets for modelling


mdata <- ward_inc %>%
  filter(tb_type=="Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(ward) %>%
  mutate(y_num = row_number()) %>%
  ungroup()


mdata_extrapulmonary <- ward_inc %>%
  filter(tb_type=="Non-Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(ward) %>%
  mutate(y_num = row_number()) %>%
  ungroup() %>% 
  filter(year<=1961) #no data for 1962 and 1963


#scaffold for overall predictions
overall_scaffold <- mdata %>%
    select(year, year2, y_num, acf_period, population_without_inst_ship, ward, cases) %>%
    group_by(year, year2, y_num, acf_period) %>%
    summarise(population_without_inst_ship = sum(population_without_inst_ship),
              cases = sum(cases)) %>%
    ungroup() %>%
    mutate(inc_100k = cases/population_without_inst_ship*100000) %>%
    left_join(mdata_extrapulmonary %>% group_by(year) %>%
                summarise(cases_extrapulmonary = sum(cases))) %>%
    mutate(inc_100k_extrapulmonary = cases_extrapulmonary/population_without_inst_ship*100000)
`summarise()` has grouped output by 'year', 'year2', 'y_num'. You can override using the `.groups` argument.Joining with `by = join_by(year)`

7. Pulmonary TB model

7.1 Fit the model

Look at the mean and variance of counts (counts of pulmonary notifications are what we are predicting)


#Mean of counts per year
mean(mdata$cases)
[1] 48.32819
#variance of counts per year
var(mdata$cases)
[1] 915.5749

Quite a bit of over-dispersion here, so negative binomial distribution might be a better choice of distributional family than Poisson.

Fit the model with the data


m_pulmonary <- brm(
  cases ~ 0 + Intercept + y_num*acf_period + (1 + y_num*acf_period | ward) + offset(log(population_without_inst_ship)),
                  data = mdata,
                  family = negbinomial(),
                  seed = 1234,
                  threads = threading(2, grainsize = 100, static = TRUE), #for exact reproducibility
                  backend = "cmdstanr",
                  chains = 4, cores = 4,
                  prior = prior(normal(0,1), class=b, coef = "Intercept") +
                          prior(gamma(0.01, 0.01), class = shape) +
                          prior(normal(0, 1), class = b) +
                          prior(exponential(1), class=sd) +
                          prior(lkj(4), class=cor),
  control = list(adapt_delta = 0.9))
Compiling Stan program...

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

-

 
Start sampling
Running MCMC with 4 parallel chains, with 2 thread(s) per chain...

Chain 1 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 3 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 4 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 3 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 1 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 4 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 2 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 3 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 1 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 4 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 3 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 2 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 3 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 2 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 1 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 4 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 3 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 1 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 2 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 4 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 3 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 2 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 1 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 2 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 4 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 1 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 2 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 3 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 4 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 2 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 1 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 3 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 4 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 1 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 2 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 2 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 3 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 3 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 1 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 4 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 3 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 3 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 1 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 1 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 4 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 2 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 3 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 1 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 4 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 3 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 1 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 4 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 2 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 1 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 2 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 3 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 4 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 1 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 3 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 2 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 4 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 1 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 2 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 4 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 2 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 1 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 4 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 3 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 3 finished in 49.5 seconds.
Chain 2 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 2 finished in 49.8 seconds.
Chain 1 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 4 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 1 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 4 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 finished in 54.7 seconds.
Chain 4 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 4 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 4 finished in 57.6 seconds.

All 4 chains finished successfully.
Mean chain execution time: 52.9 seconds.
Total execution time: 57.8 seconds.
Loading required package: rstan
Loading required package: StanHeaders
Warning: package ‘StanHeaders’ was built under R version 4.3.3
rstan version 2.32.6 (Stan version 2.32.2)

For execution on a local, multicore CPU with excess RAM we recommend calling
options(mc.cores = parallel::detectCores()).
To avoid recompilation of unchanged Stan programs, we recommend calling
rstan_options(auto_write = TRUE)
For within-chain threading using `reduce_sum()` or `map_rect()` Stan functions,
change `threads_per_chain` option:
rstan_options(threads_per_chain = 1)


Attaching package: ‘rstan’

The following object is masked from ‘package:tidyr’:

    extract
  
#check model diagnostics
summary(m_pulmonary)
 Family: negbinomial 
  Links: mu = log; shape = identity 
Formula: cases ~ 0 + Intercept + y_num * acf_period + (1 + y_num * acf_period | ward) + offset(log(population_without_inst_ship)) 
   Data: mdata (Number of observations: 518) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Multilevel Hyperparameters:
~ward (Number of levels: 37) 
                                                      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept)                                             0.25      0.03     0.19     0.33 1.00     1041     2023
sd(y_num)                                                 0.02      0.01     0.01     0.03 1.00      631      579
sd(acf_periodb.acf)                                       0.06      0.04     0.00     0.16 1.00     1506     1729
sd(acf_periodc.postMacf)                                  0.12      0.06     0.01     0.24 1.00      775     1331
sd(y_num:acf_periodb.acf)                                 0.01      0.01     0.00     0.02 1.00     1167     1599
sd(y_num:acf_periodc.postMacf)                            0.01      0.01     0.00     0.02 1.00      521     1213
cor(Intercept,y_num)                                     -0.45      0.19    -0.75    -0.02 1.00     1316     1423
cor(Intercept,acf_periodb.acf)                           -0.23      0.29    -0.70     0.38 1.00     2722     2557
cor(y_num,acf_periodb.acf)                               -0.04      0.27    -0.55     0.50 1.00     4075     3006
cor(Intercept,acf_periodc.postMacf)                      -0.14      0.24    -0.57     0.38 1.00     2896     2380
cor(y_num,acf_periodc.postMacf)                           0.10      0.25    -0.42     0.57 1.00     2233     2626
cor(acf_periodb.acf,acf_periodc.postMacf)                 0.06      0.27    -0.47     0.56 1.00     1953     2391
cor(Intercept,y_num:acf_periodb.acf)                     -0.24      0.28    -0.71     0.36 1.00     2295     2772
cor(y_num,y_num:acf_periodb.acf)                         -0.04      0.26    -0.52     0.47 1.00     5057     3047
cor(acf_periodb.acf,y_num:acf_periodb.acf)               -0.05      0.29    -0.59     0.50 1.00     4064     3022
cor(acf_periodc.postMacf,y_num:acf_periodb.acf)           0.07      0.27    -0.49     0.56 1.00     3179     3431
cor(Intercept,y_num:acf_periodc.postMacf)                -0.02      0.26    -0.52     0.49 1.00     3955     2979
cor(y_num,y_num:acf_periodc.postMacf)                    -0.03      0.28    -0.56     0.52 1.00     2265     3079
cor(acf_periodb.acf,y_num:acf_periodc.postMacf)           0.05      0.28    -0.51     0.56 1.00     2269     2897
cor(acf_periodc.postMacf,y_num:acf_periodc.postMacf)     -0.08      0.29    -0.62     0.49 1.00     2520     2895
cor(y_num:acf_periodb.acf,y_num:acf_periodc.postMacf)     0.06      0.28    -0.50     0.58 1.00     1895     2833

Regression Coefficients:
                           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept                     -6.14      0.05    -6.23    -6.04 1.00      795     1674
y_num                         -0.02      0.01    -0.03    -0.01 1.00     2412     2841
acf_periodb.acf                0.01      1.00    -1.98     1.91 1.00     3260     2784
acf_periodc.postMacf           0.04      0.11    -0.17     0.25 1.00     2944     3110
y_num:acf_periodb.acf          0.08      0.12    -0.16     0.33 1.00     3281     2725
y_num:acf_periodc.postMacf    -0.05      0.01    -0.07    -0.03 1.00     2649     2847

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
shape    92.15     20.99    60.74   142.29 1.00     2682     2250

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
plot(m_pulmonary)


pp_check(m_pulmonary, type='ecdf_overlay')
Using 10 posterior draws for ppc type 'ecdf_overlay' by default.

prior_summary(m_pulmonary)
                prior class                       coef group resp dpar nlpar lb ub       source
         normal(0, 1)     b                                                                user
         normal(0, 1)     b            acf_periodb.acf                             (vectorized)
         normal(0, 1)     b       acf_periodc.postMacf                             (vectorized)
         normal(0, 1)     b                  Intercept                                     user
         normal(0, 1)     b                      y_num                             (vectorized)
         normal(0, 1)     b      y_num:acf_periodb.acf                             (vectorized)
         normal(0, 1)     b y_num:acf_periodc.postMacf                             (vectorized)
 lkj_corr_cholesky(4)     L                                                                user
 lkj_corr_cholesky(4)     L                             ward                       (vectorized)
       exponential(1)    sd                                                   0            user
       exponential(1)    sd                             ward                  0    (vectorized)
       exponential(1)    sd            acf_periodb.acf  ward                  0    (vectorized)
       exponential(1)    sd       acf_periodc.postMacf  ward                  0    (vectorized)
       exponential(1)    sd                  Intercept  ward                  0    (vectorized)
       exponential(1)    sd                      y_num  ward                  0    (vectorized)
       exponential(1)    sd      y_num:acf_periodb.acf  ward                  0    (vectorized)
       exponential(1)    sd y_num:acf_periodc.postMacf  ward                  0    (vectorized)
    gamma(0.01, 0.01) shape                                                   0            user

Nicer version of trace plots for supplemental material


as_draws_df(m_pulmonary) %>% 
  bayesplot::mcmc_rank_overlay(pars = vars(b_Intercept:shape),
             facet_args = list(ncol = 4)) +
  scale_colour_scico_d(palette = "managua", name = "Chain") +
  theme_ggdist()+
  theme(panel.border = element_rect(colour = "grey78", fill=NA),
        legend.position = "top")
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

ggsave(here("figures/s8.tiff"), width=16, height=16)

Nicer version of table of parameters for supplement


summarise_draws(m_pulmonary) %>%
  mutate(across(c(mean:ess_tail), comma, accuracy=0.01)) %>%
  write_csv(here("figures/s9_table.csv"))
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `across(c(mean:ess_tail), comma, accuracy = 0.01)`.
Caused by warning:
! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
Supply arguments directly to `.fns` through an anonymous function instead.

  # Previously
  across(a:b, mean, na.rm = TRUE)

  # Now
  across(a:b, \(x) mean(x, na.rm = TRUE))
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

7.2 Summarise change in CNRs

Summarise the posterior in graphical form


f1b <- plot_counterfactual(model_data = overall_scaffold, model = m_pulmonary, 
                           population_denominator = population_without_inst_ship, outcome = inc_100k, grouping_var=NULL,
                           re_formula = NA)
  
f1b

Make this into a figure combined with the map of empirical data


f1a <- st_as_sf(left_join(ward_inc, glasgow_wards_1951)) %>%
  filter(tb_type=="Pulmonary") %>%
  ggplot() +
  geom_sf(aes(fill=inc_100k), colour="grey98", lwd=0.01) +
  facet_wrap(year~., ncol = 7) +
  scale_fill_scico(name="CNR (per 100,000)",
                       palette = "acton", direction = -1) +
  theme_grey() +
  theme(legend.position = "top",
        #legend.key.width = unit(1, "cm"),
        legend.title.align = 0.5,
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.line = element_blank(),
        axis.ticks = element_blank(),
        panel.background = element_blank(),
        legend.title = element_text(size=10))
Joining with `by = join_by(division, ward, ward_number)`Warning: The `legend.title.align` argument of `theme()` is deprecated as of ggplot2 3.5.0.
Please use theme(legend.title = element_text(hjust)) instead.
(f1a / f1b) + plot_annotation(tag_levels = "A")

ggsave(here("figures/f1.tiff"), width=7, height=8)

Summary of change in notifications numerically


overall_change <- summarise_change(model_data=overall_scaffold, model=m_pulmonary, 
                                   population_denominator=population_without_inst_ship, grouping_var=NULL, re_formula = NA)
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
overall_change %>%
  keep((names(.) %in% tokeep)) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
NA
NA

7.3 Compared to counterfactual

Numbers of pulmonary TB cases averted compared to counterfactual per year.


overall_pulmonary_counterf <- calculate_counterfactual(model_data = overall_scaffold, model=m_pulmonary, population_denominator = population_without_inst_ship)
Joining with `by = join_by(year, population_without_inst_ship, .draw)`Joining with `by = join_by(.draw)`
overall_pulmonary_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

Total pulmonary TB cases averted between 1958 and 1963


overall_pulmonary_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

7.4 Correlation between RR.peak, RR.level, and RR.slope

What are the correlations between peak, level, and slope?


#RR.peak histogram
a <- overall_change$peak_draws %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.peak",
       y="")

#RR. level histogram
b <- overall_change$level_draws  %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.level",
       y="")

#RR.slope histogram
c <- overall_change$slope_draws %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +  
  #scale_x_continuous(limits = c(0, 6)) +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.slope",
       y="")


#Correlation between RR.peak and RR.level
cor_rr_peak_rr_level <- round(cor(pluck(overall_change$peak_draws$estimate), pluck(overall_change$level_draws$estimate)), digits = 2)

#Correlation between RR.peak and RR.slope
cor_rr_peak_rr_slope <- round(cor(pluck(overall_change$peak_draws$estimate), pluck(overall_change$slope_draws$estimate)), digits = 2)

#Correlation between RR.level and RR.slope
cor_rr_level_rr_slope <- round(cor(pluck(overall_change$level_draws$estimate), pluck(overall_change$slope_draws$estimate)), digits = 2)


#plot of correlation between RR.peak and RR.level
d <- bind_cols(RR.peak=pluck(overall_change$peak_draws$estimate), 
          RR.level =pluck(overall_change$level_draws$estimate)) %>%
  ggplot(aes(y=RR.peak, x = RR.level)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=2.2, x=0.58, label=cor_rr_peak_rr_level), colour="firebrick")  +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))

#plot of correlation between RR.peak and RR.slope
e <- bind_cols(RR.peak=pluck(overall_change$peak_draws$estimate), 
          RR.slope =pluck(overall_change$slope_draws$estimate)) %>%
  ggplot(aes(y=RR.peak, x = RR.slope)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=2.1, x=0.65, label=cor_rr_peak_rr_slope), colour="firebrick")  +
  #scale_x_continuous(limits = c(0, 6)) +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))

#plot of correlation between RR.level and RR.slope
f <- bind_cols(RR.level=pluck(overall_change$level_draws$estimate), 
          RR.slope =pluck(overall_change$slope_draws$estimate)) %>%
  ggplot(aes(y=RR.level, x = RR.slope)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=0.75, x=0.65, label=cor_rr_level_rr_slope), colour="firebrick")  +  
  #scale_x_continuous(limits = c(0, 6)) +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))


(plot_spacer() + plot_spacer() + c) /
  (plot_spacer() + b + f) /
  (a + d + e)

ggsave(here("figures/s10.tiff"), width=8, height=8)

NA
NA
NA

7.5 Ward level pulmonary TB estimates

Plot the counterfactual at ward level


plot_counterfactual(model_data = mdata, model=m_pulmonary, outcome = inc_100k, population_denominator = population_without_inst_ship, 
                    grouping_var = ward, ward, re_formula= ~(1 + y_num*acf_period | ward)) +
  scale_y_continuous(limits = c(0,500))
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
  
ggsave(here("figures/s7.tiff"), width=16, height=12)

Summary of change in notifications at ward level


ward_change <- summarise_change(model_data=mdata, model=m_pulmonary, 
                                   population_denominator=population_without_inst_ship, grouping_var=ward, 
                                   re_formula = ~(1 + y_num*acf_period | ward))
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw', 'y_num'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
ward_change %>%
  keep((names(.) %in% tokeep)) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()


#plot these in a figure
ward_effects <- ward_change %>%
  keep((names(.) %in% tokeep)) %>%
  bind_rows() %>%
  bind_rows(overall_change$peak_summary) %>%
  bind_rows(overall_change$level_summary) %>%
  bind_rows(overall_change$slope_summary) %>%
  mutate_at(.vars = vars(estimate:.upper), 
            .funs = funs(as.numeric)) %>%
  select(measure, everything()) %>%
  mutate(estimate = as.double(estimate)) %>%
  full_join(glasgow_wards_1951) %>% 
  mutate(ward2 = paste0(ward_number, ". ", ward)) %>%
  mutate(ward2 = case_when(is.na(ward) ~ "Overall",
                          TRUE ~ ward2)) %>%
  st_as_sf() 
Warning: `funs()` was deprecated in dplyr 0.8.0.
Please use a list of either functions or lambdas: 

  # Simple named list: 
  list(mean = mean, median = median)

  # Auto named with `tibble::lst()`: 
  tibble::lst(mean, median)

  # Using lambdas
  list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))Joining with `by = join_by(ward)`
#function for plotting choropleth maps
plot_ward_effect <- function(data, measure){
  {{data}} %>%
  filter(measure == {{measure}}) %>%
  ggplot() +
  geom_sf(aes(fill=estimate)) +
  geom_sf_label(aes(label = ward_number), size=3, fill=NA, label.size = NA, colour="black") +
  scale_fill_scico(trans="log", palette = "roma", midpoint = 0, limits=c(0.5,2.25),
                       breaks = c(0.5, 0.75, 1, 1.5, 2, 2.5), labels = c(0.5, 0.75, 1, 1.5, 2, 2.5),
                   name="Relative rate") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA),
        axis.text.x=element_text(angle=45, hjust=1)) +
    labs(x="", y="")
}

#function for plotting catapiller plots
plot_ward_cat <- function(data, measure, scale){

    ggplot() +
    geom_hline(aes(yintercept=1), linetype=2) +
    geom_pointrange(data = {{data}} %>%     
                      filter(measure=={{measure}}) %>%
                      filter(!is.na(ward)),
                    aes(y=estimate, ymin=.lower, ymax=.upper, 
                      x=fct_reorder(ward2, estimate), colour=estimate)) +
    geom_pointrange(data = {{data}} %>% 
                      filter(measure=={{measure}}) %>%
                      filter(is.na(ward)),
                    aes(y=estimate, ymin=.lower, ymax=.upper, 
                      x=ward2), colour="black") +
    coord_flip() +
    scale_colour_scico(trans="log", palette = "roma", midpoint = 0, limits=c(0.5,2.25), 
                       breaks = c(0.5, 0.75, 1, 1.5, 2, 2.5), labels = c(0.5, 0.75, 1, 1.5, 2, 2.5),
                       name="Relative rate") +
    scale_y_continuous() +
    theme_ggdist()  +
    theme(panel.border = element_rect(colour = "grey78", fill=NA)) +
    labs(x = "",
         y = "Relative rate (95% UI)")+
    guides(x = "axis_truncated", y = "axis_truncated")
}



ward_peak_i <- plot_ward_effect(data = ward_effects, measure = "RR.peak") + ggtitle("Peak effect")
ward_level_i <- plot_ward_effect(data = ward_effects, measure = "RR.level") + ggtitle("Level effect")
ward_slope_i <- plot_ward_effect(data = ward_effects, measure = "RR.slope") + ggtitle("Slope effect")

ward_peak_ii <- plot_ward_cat(data = ward_effects, measure = "RR.peak") + ggtitle("Peak effect")
ward_level_ii <- plot_ward_cat(data = ward_effects, measure = "RR.level") + ggtitle("Level effect")
ward_slope_ii <- plot_ward_cat(data = ward_effects, measure = "RR.slope") + ggtitle("Slope effect")

s4 <- (ward_peak_i + ward_level_i + ward_slope_i) /
  (ward_peak_ii + ward_level_ii + ward_slope_ii)

s4[[1]] <- s4[[1]] + plot_layout(tag_level = 'new')
s4[[2]] <- s4[[2]] + plot_layout(tag_level = 'new')
s4 + plot_annotation(tag_levels = c('A', '1')) + plot_layout(guides = 'collect') &
  theme(legend.position='bottom',
        legend.key.width = unit(3, "cm"))


ggsave(here("figures/f2.tiff"), width = 16, height=12)

Calculate the counterfactual per ward


ward_pulmonary_counterf <- calculate_counterfactual(model_data = mdata, model=m_pulmonary, 
                                                    population_denominator = population_without_inst_ship,
                                                    grouping_var = ward, re_formula=~(1 + y_num*acf_period | ward))
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.Joining with `by = join_by(year, population_without_inst_ship, .draw, ward)`Joining with `by = join_by(.draw, ward)`
ward_pulmonary_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

Overall counterfactual per ward


ward_pulmonary_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA

8. Extra-pulmonary TB notifications

8.1 Fit the model

pp_check(m_extrapulmonary, type='ecdf_overlay')
Using 10 posterior draws for ppc type 'ecdf_overlay' by default.

8.2 Summary of change

Summarise in plot

ggsave(here("figures/s11.tiff"), width=10)
Saving 10 x 7 in image

Summarise numerically.

8.3 Compared to counterfactual

Numbers of extra-pulmonary TB cases averted overall.

Total extrapulmonary TB cases averted between 1958 and 1963

Make into Table 2

bind_rows(
overall_pulmonary_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "PTB_ward"),

overall_pulmonary_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "PTB_overall"),

overall_ep_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "EPTB"),

overall_ep_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(model = "EPTB overall")

) %>%
  select(model, year, diff_inc100k, diff_inc100k.lower:rr_inc100k.upper, 
         cases_averted:cases_averted.upper,
         pct_change:pct_change.upper) %>%
  transmute(model=model, year=year,
            diff_cnr = glue("{diff_inc100k} [{diff_inc100k.lower}, {diff_inc100k.upper}]"),
            rr = glue("{rr_inc100k} [{rr_inc100k.lower}, {rr_inc100k.upper}]"),
            cases_averted = glue("{cases_averted} [{cases_averted.lower}, {cases_averted.upper}]"),
            pct_change = glue("{pct_change} [{pct_change.lower}, {pct_change.upper}]")) %>%
  write_csv(here("figures/table2.csv"))
Error: object 'overall_ep_counterf' not found

8.4 Ward-level extra-pulmonary summaries

Ward-level extra-pulmonary estimates in graphical form.

plot_counterfactual(model_data = mdata_extrapulmonary, model=m_extrapulmonary, outcome = inc_100k, 
                    population_denominator = population_without_inst_ship, grouping_var = ward,re_formula =~(y_num*acf_period | ward), 
                    ward) + scale_y_continuous(limits= c(0,75))
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.

Numerical summary.

9. Age-sex model

9.1 FIt the model

Fit the model

(Not rewritten the functions for this yet)


mdata_age_sex <- cases_by_age_sex %>%
  filter(tb_type=="Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  mutate(year2 = year+0.5) %>%
  group_by(age, sex) %>%
  mutate(y_num = row_number()) %>%
  ungroup()

m_age_sex <- brm(
  cases ~ y_num + (acf_period)*(age*sex) + (acf_period:y_num)*(age*sex),
                  data = mdata_age_sex,
                  family = negbinomial(),
                  seed = 1234,
                  threads = threading(2, grainsize = 100, static = TRUE), #for exact reproducibility
                  backend = "cmdstanr",
                  chains = 4, cores = 4, 
                  prior = prior(normal(0,1), class = Intercept) +
                          prior(gamma(0.01, 0.01), class = shape) +
                          prior(normal(0, 1), class = b))

summary(m_age_sex)
plot(m_age_sex)
pp_check(m_age_sex, type='ecdf_overlay')

Summarise posterior


#posterior draws, and summarise
age_sex_summary <- mdata_age_sex %>%
  select(year, year2, y_num, acf_period, age, sex) %>%
  add_epred_draws(m_age_sex) %>%
  group_by(year2, acf_period, age, sex) %>%
  mean_qi() %>%
  mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="c. post-acf" ~ "Post Intervention"))

#create the counterfactual (no intervention), and summarise
age_sex_counterfact <- 
  tibble(year = mdata_age_sex$year,
         year2 = mdata_age_sex$year2,
         y_num = mdata_age_sex$y_num,
         age = mdata_age_sex$age,
         sex = mdata_age_sex$sex,
         acf_period = factor("a. pre-acf")) %>%
  add_epred_draws(m_age_sex) %>%
  group_by(year2, acf_period, age, sex) %>%
  mean_qi() %>%
  mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="c. post-acf" ~ "Post Intervention")) %>%
  ungroup() %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  mutate(sex = case_when(sex== "M" ~ "Male",
                         sex== "F" ~ "Female")) 



age_sex_summary %>%
  ungroup() %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  mutate(sex = case_when(sex== "M" ~ "Male",
                         sex== "F" ~ "Female")) %>%
  ggplot() +
  geom_ribbon(aes(ymin=.epred.lower, ymax=.epred.upper, x=year2, group = acf_period, fill=acf_period), alpha=0.5) +
  geom_ribbon(data = age_sex_counterfact %>% filter(year>=1956), 
              aes(ymin=.epred.lower, ymax=.epred.upper, x=year2, fill="Counterfactual"), alpha=0.5) +
  geom_line(data = age_sex_counterfact %>% filter(year>=1956), 
              aes(y=.epred, x=year2, colour="Counterfactual")) +
  geom_line(aes(y=.epred, x=year2, group=acf_period,  colour=acf_period)) +
  geom_point(data = mdata_age_sex %>%
  ungroup() %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  mutate(sex = case_when(sex== "M" ~ "Male",
                         sex== "F" ~ "Female")) %>%
  mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="b. acf" ~ "Counterfactual",
                                  acf_period=="c. post-acf" ~ "Post Intervention"))%>%
                 mutate(acf_period2 = case_when(acf_period=="Before Intervention" ~ "Pre-ACF",
                                               acf_period=="Counterfactual" ~ "ACF",
                                               acf_period=="Post Intervention" ~ "Post-ACF")), 
  aes(y=cases, x=year2, shape=fct_relevel(acf_period2,
                                                             "Pre-ACF",
                                                             "ACF",
                                                             "Post-ACF")), size=2) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  facet_grid(fct_relevel(age, "65 & up y", "56 to 65y", "46 to 55y", "36 to 45y", "26 to 35y", "16 to 25y", "06 to 15y", "0 to 5y")~sex, 
             scales = "free_y") +
  theme_ggdist() +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:") +
  scale_colour_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="Model estimates:") +
  scale_shape_discrete(name="Emprical data (period):", na.translate = F) +
  labs(
    x = "Year",
    y = "Case notifications (n)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme(legend.position = "bottom",          
        legend.box="vertical", 
        panel.border = element_rect(colour = "grey78", fill=NA))
  
ggsave(here("figures/s14.tiff"), height=10)

9.2 Summary of impact of intervention

Calculate summary effects


#Peak
out_age_sex_1 <- crossing(mdata_age_sex %>% 
                      select(y_num, age, sex) %>%
                      filter(y_num == 8),
                      acf_period = c("a. pre-acf", "b. acf"))

peak_draws_age_sex <- add_epred_draws(newdata = out_age_sex_1,
                  object = m_age_sex) %>%
    group_by(.draw, age, sex) %>%
    summarise(estimate = last(.epred)/first(.epred)) %>%
    ungroup() %>%
    mutate(measure = "RR.peak")
  
peak_summary_age_sex <- peak_draws_age_sex %>%
    group_by(age, sex) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.peak")


#Level
 
out_age_sex_2 <- crossing(mdata_age_sex %>% 
                      select(y_num, age, sex) %>%
                      filter(y_num == 9),
                      acf_period = c("a. pre-acf", "c. post-acf"))
  
level_draws_age_sex <- add_epred_draws(newdata = out_age_sex_2,
                  object = m_age_sex) %>%
    arrange(y_num, .draw) %>%
    group_by(.draw, age, sex) %>%
    summarise(estimate = last(.epred)/first(.epred)) %>%
    ungroup() %>%
    mutate(measure = "RR.level")
  
level_summary_age_sex <- level_draws_age_sex %>%
    group_by(age, sex) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.level")

#Slope

out_age_sex_3 <- crossing(mdata_age_sex %>% 
                      select(y_num, age, sex) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf"))
  
slope_draws_age_sex <- add_epred_draws(newdata = out_age_sex_3,
                  object = m_age_sex) %>%
        arrange(y_num) %>%
        ungroup() %>%
        group_by(.draw, y_num, age, sex) %>%
        summarise(slope = last(.epred)/first(.epred)) %>%
        ungroup() %>%
        group_by(.draw, age, sex) %>%
        summarise(estimate = last(slope)/first(slope)) %>%
        mutate(measure = "RR.slope")
  
slope_summary_age_sex <- slope_draws_age_sex %>%
     group_by(age, sex) %>%
      median_qi(estimate) %>%
      mutate(measure = "RR.slope")

Numerical summary of these summary results


bind_rows(
  peak_summary_age_sex, level_summary_age_sex, slope_summary_age_sex
) %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()

As a figure


peak_g_age_sex <- peak_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

#level plot
level_g_age_sex <- level_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

#slope plot
slope_g_age_sex <- slope_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

9.3 Compared to counterfactual


counterfact_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex) %>%
                                    mutate(acf_period = "a. pre-acf")) %>%
      filter(year>1957) %>%
      select(year, age, sex, .draw, .epred_counterf = .epred)
  
#Calcuate predicted number of cases per draw, then summarise.
post_change_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex, acf_period)) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, age, sex, .draw, .epred) 
  
#for the overall period
counterfact_overall_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex) %>%
                                    mutate(acf_period = "a. pre-acf")) %>%
      filter(year>1957) %>%
      select(age, sex, .draw, .epred)  %>%
      group_by(age, sex, .draw) %>%
      summarise(.epred_counterf = sum(.epred)) %>%
      mutate(year = "Overall (1958-1963)")
  
#Calcuate incidence per draw, then summarise.
post_change_overall_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex, acf_period)) %>%
      filter(year>1957) %>%
      select(age, sex, .draw, .epred) %>%
      group_by(.draw, age, sex) %>%
      summarise(.epred = sum(.epred)) 
  
counter_post_overall_age_sex <-
  left_join(counterfact_overall_age_sex, post_change_overall_age_sex) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf) %>%
    group_by(age, sex) %>%
    mean_qi(cases_averted, pct_change) %>%
    ungroup() %>%
    mutate(year = "Overall (1958-1963)") 


age_sex_txt <- counter_post_overall_age_sex %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  transmute(year = as.character(year),
            sex = sex,
            age = age,
            cases_averted = glue::glue("{cases_averted}\n({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change}\n({pct_change.lower} to {pct_change.upper})"))


age_sex_txt %>% datatable()

counterfactual_g_age_sex <- counter_post_overall_age_sex %>% 
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_pointrange(aes(x = age, y=cases_averted, ymin=cases_averted.lower, ymax=cases_averted.upper, colour=sex, shape=sex), position=position_dodge(width=0.5)) + 
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  scale_y_continuous(labels = comma) +
  labs(x="",
       y="Number (95% UI)",
       colour="") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA),
        legend.position = "bottom")

counterfactual_g_age_sex

Join together for Figure 3.


(peak_g_age_sex + level_g_age_sex) / (slope_g_age_sex + counterfactual_g_age_sex) + plot_annotation(tag_levels = "A") + plot_layout(guides = "collect") & theme(legend.position = "bottom")

ggsave(here("figures/f3.tiff"), width = 12, height=8)
LS0tCnRpdGxlOiAiR2xhc2dvdyBUQiBBQ0YiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyAxLiBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKIyMjIyAxLjEgTGlicmFyaWVzCgpMb2FkIHRoZSByZXF1aXJlZCBsaWJyYXJpZXMuCgpgYGB7ciwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShEVCkKbGlicmFyeShicm1zKQpsaWJyYXJ5KHRpZHliYXllcykKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkobWFyZ2luYWxlZmZlY3RzKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoc2NpY28pCmxpYnJhcnkoZ2dkZW5zaXR5KQpsaWJyYXJ5KGdncHVicikKbGlicmFyeSh1bml0cykKbGlicmFyeShnbHVlKQpsaWJyYXJ5KGdnaDR4KQoKYGBgCgojIyMjIDEuMiBIZWxwZXIgZnVuY3Rpb25zCgpGdW5jdGlvbnMgdGhhdCB3ZSB3aWxsIHVzZSB0aHJvdWdob3V0IHRoZSBzY3JpcHQKCmBgYHtyfQojbGFiZWxsZXIgZm9yIHllYXJzCnllYXJfbGFiZWxzIDwtIGMoMTk1MDoxOTYzKQoKI1RoZSBHbGFzZ293IG1hc3MgbWludXR1cmUgY2hlc3QgWC1yYXkgY2FtcGFpZ24gaGFwcGVuZWQgYmV0d2VlbiAxMXRoIE1hcmNoIGFuZCAxMnRoIEFwcmlsIDE5NTcKI1NlZ21lbnQgZm9yIGdyYXBocyB0byBtYXRjaCBBQ0YgcGVyaW9kCmFjZl9zdGFydCA8LSBkZWNpbWFsX2RhdGUoeW1kKCIxOTU3LTAzLTExIikpCmFjZl9lbmQgPC0gZGVjaW1hbF9kYXRlKHltZCgiMTk1Ny0wNC0xMiIpKQoKCmBgYAoKRnVuY3Rpb24gZm9yIGNvdW50ZXJmYWN0dWFsIHBsb3RzCgpgYGB7cn0KCgpwbG90X2NvdW50ZXJmYWN0dWFsIDwtIGZ1bmN0aW9uKG1vZGVsX2RhdGEsIG1vZGVsLCBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yLCBvdXRjb21lLCBncm91cGluZ192YXI9TlVMTCwgcmVfZm9ybXVsYSwuLi4pewogIAogICNsYWJlbGxlciBmb3IgeWVhcnMKICB5ZWFyX2xhYmVscyA8LSBjKDE5NTA6MTk2NCkgI2V4dHJhIHllYXIgZm9yIHRoZSBleHRhbnQgb2YgdGhlIHgtYXhpcwoKICAjVGhlIEdsYXNnb3cgbWFzcyBtaW51dHVyZSBjaGVzdCBYLXJheSBjYW1wYWlnbiBoYXBwZW5lZCBiZXR3ZWVuIDExdGggTWFyY2ggYW5kIDEydGggQXByaWwgMTk1NwogICNTZWdtZW50IGZvciBncmFwaHMgdG8gbWF0Y2ggQUNGIHBlcmlvZAogIGFjZl9zdGFydCA8LSBkZWNpbWFsX2RhdGUoeW1kKCIxOTU3LTAzLTExIikpCiAgYWNmX2VuZCA8LSBkZWNpbWFsX2RhdGUoeW1kKCIxOTU3LTA0LTEyIikpCgogIHN1bW1hcnkgPC0ge3ttb2RlbF9kYXRhfX0gJT4lCiAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhY2ZfcGVyaW9kLCB7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwge3tvdXRjb21lfX0sIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgYWRkX2VwcmVkX2RyYXdzKHt7bW9kZWx9fSwgcmVfZm9ybXVsYT17e3JlX2Zvcm11bGF9fSkgJT4lCiAgICBncm91cF9ieSh5ZWFyMiwgYWNmX3BlcmlvZCwge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICBtZWFuX3FpKCkgJT4lCiAgICBtdXRhdGUoLmVwcmVkX2luYyA9IC5lcHJlZC97e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSoxMDAwMDAsCiAgICAgICAgICAuZXByZWRfaW5jLmxvd2VyID0gLmVwcmVkLmxvd2VyL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCwKICAgICAgICAgIC5lcHJlZF9pbmMudXBwZXIgPSAuZXByZWQudXBwZXIve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwKSAlPiUKICAgIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKGFjZl9wZXJpb2Q9PSJhLiBwcmUtYWNmIiB+ICJCZWZvcmUgSW50ZXJ2ZW50aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSkKCgoKICAjY3JlYXRlIHRoZSBjb3VudGVyZmFjdHVhbCAobm8gaW50ZXJ2ZW50aW9uKSwgYW5kIHN1bW1hcmlzZQogIAogIGNvdW50ZXJmYWN0IDwtCiAgICBhZGRfZXByZWRfZHJhd3Mob2JqZWN0ID0ge3ttb2RlbH19LAogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5ZWFyLCB5ZWFyMiwgeV9udW0sIHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB7e2dyb3VwaW5nX3Zhcn19LCB7e291dGNvbWV9fSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIiksIHJlX2Zvcm11bGE9e3tyZV9mb3JtdWxhfX0pICU+JQogICAgZ3JvdXBfYnkoeWVhcjIsIGFjZl9wZXJpb2QsIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgbWVhbl9xaSgpICU+JQogICAgbXV0YXRlKC5lcHJlZF9pbmMgPSAuZXByZWQve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwLAogICAgICAgICAuZXByZWRfaW5jLmxvd2VyID0gLmVwcmVkLmxvd2VyL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCwKICAgICAgICAgLmVwcmVkX2luYy51cHBlciA9IC5lcHJlZC51cHBlci97e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSoxMDAwMDApICU+JQogICAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIkJlZm9yZSBJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSkKICAKCgogICNwbG90IHRoZSBpbnRlcnZlbnRpb24gZWZmZWN0CnAgPC0gc3VtbWFyeSAlPiUKICAgIGRyb3BsZXZlbHMoKSAlPiUKICAgIGdncGxvdCgpICsKICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0LCBsaW5ldHlwZT0iTWFzcyBDWFIgc2NyZWVuaW5nIGludGVydmVudGlvbiIpKSArCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQsIGxpbmV0eXBlPSJNYXNzIENYUiBzY3JlZW5pbmcgaW50ZXJ2ZW50aW9uIikpICsKICAgIGdlb21fcmliYm9uKGFlcyh5bWluPS5lcHJlZF9pbmMubG93ZXIsIHltYXg9LmVwcmVkX2luYy51cHBlciwgeD15ZWFyMiwgZ3JvdXAgPSBhY2ZfcGVyaW9kLCBmaWxsPWFjZl9wZXJpb2QpLCBhbHBoYT0wLjUpICsKICAgIGdlb21fcmliYm9uKGRhdGEgPSBjb3VudGVyZmFjdCAlPiUgZmlsdGVyKHllYXI+PTE5NTYpLCAKICAgICAgICAgICAgICAgIGFlcyh5bWluPS5lcHJlZF9pbmMubG93ZXIsIHltYXg9LmVwcmVkX2luYy51cHBlciwgeD15ZWFyMiwgZmlsbD0iQ291bnRlcmZhY3R1YWwiKSwgYWxwaGE9MC41KSArCiAgICBnZW9tX2xpbmUoZGF0YSA9IGNvdW50ZXJmYWN0ICU+JSBmaWx0ZXIoeWVhcj49MTk1NiksIAogICAgICAgICAgICAgIGFlcyh5PS5lcHJlZF9pbmMsIHg9eWVhcjIsIGNvbG91cj0iQ291bnRlcmZhY3R1YWwiKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5PS5lcHJlZF9pbmMsIHg9eWVhcjIsIGdyb3VwPWFjZl9wZXJpb2QsICBjb2xvdXI9YWNmX3BlcmlvZCkpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHt7bW9kZWxfZGF0YX19ICU+JQogICAgICAgICAgICAgICAgIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKGFjZl9wZXJpb2Q9PSJhLiBwcmUtYWNmIiB+ICJQcmUtQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kPT0iYi4gYWNmIiB+ICJBQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdC1BQ0YiKSksIAogICAgICAgICAgICAgICBhZXMoeT17e291dGNvbWV9fSwgeD15ZWFyMiwgc2hhcGU9ZmN0X3JlbGV2ZWwoYWNmX3BlcmlvZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcmUtQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBvc3QtQUNGIikpLCBzaXplPTIpICsKICAgIHRoZW1lX2dyZXkoKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hLCBsaW1pdHMgPWMoMCw0MDApKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSAsIG5hbWU9Ik1vZGVsIGVzdGltYXRlczoiLCBuYS50cmFuc2xhdGUgPSBGKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSAsIG5hbWU9Ik1vZGVsIGVzdGltYXRlczoiLCBuYS50cmFuc2xhdGUgPSBGKSArCiAgICBzY2FsZV9zaGFwZV9kaXNjcmV0ZShuYW1lPSJFbXBpcmljYWwgZGF0YSAocGVyaW9kKToiLCBuYS50cmFuc2xhdGUgPSBGKSArCiAgICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gMiwgbmFtZT0iIikgKwogICAgbGFicygKICAgICAgeCA9ICIiLAogICAgICB5ID0gIkNOUiAocGVyIDEwMCwwMDApIgogICAgKSArCiAgICBndWlkZXMoeCA9ICJheGlzX3RydW5jYXRlZCIsIHkgPSAiYXhpc190cnVuY2F0ZWQiKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgIGxlZ2VuZC5ib3g9InZlcnRpY2FsIiwgCiAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgYW5nbGUgPSA5MCwgaGp1c3Q9MSwgdmp1c3Q9MC41KSwKICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoMC4xLCAnY20nKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSkgCgogICAgZmFjZXRfdmFycyA8LSB2YXJzKC4uLikKCiAgaWYgKGxlbmd0aChmYWNldF92YXJzKSAhPSAwKSB7CiAgICBwIDwtIHAgKyBmYWNldF93cmFwKGZhY2V0X3ZhcnMpCiAgfQogIHAKCn0KCmBgYAoKRnVuY3Rpb24gZm9yIGNhbGN1bGF0aW5nICBtZWFzdXJlcyBvZiBjaGFuZ2Ugb3ZlciB0aW1lIChSUi5wZWFrLCBSUi5sZXZlbCwgUlIuc2xvcGUpCgoKYGBge3J9CgpzdW1tYXJpc2VfY2hhbmdlIDwtIGZ1bmN0aW9uKG1vZGVsX2RhdGEsIG1vZGVsLCBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yLCBncm91cGluZ192YXIgPSBOVUxMLCByZV9mb3JtdWxhID0gTlVMTCkgewogIAogICNmdW5jdGlvbnMgZm9yIGNhbGN1bGF0aW5nIFJSLnBlYWsKICAjaS5lLiByZWxhdGl2ZSBjYXNlIG5vdGlmaWNhdGlvbiByYXRlIGluIDE5NTcgdnMuIGNvdW50ZXJmYWN0dWFsIHRyZW5kIGZvciAxOTU3CiAgCiAgZ3JvdXBpbmdfdmFyIDwtIGVucXVvKGdyb3VwaW5nX3ZhcikKICAKICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIAogICAgI21ha2UgdGhlIHByZWRpY3Rpb24gbWF0cml4LCBjb25kaXRpb25hbCBvbiB3aGV0aGVyIHdlIHdhbnQgcmFuZG9tIGVmZmVjdHMgaW5jbHVkZWQgb3Igbm90LgogICAgb3V0IDwtIGNyb3NzaW5nKHt7bW9kZWxfZGF0YX19ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgeV9udW0sICEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih5X251bSA9PSA4KSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJiLiBhY2YiKQogICAgKQogIH0gZWxzZSB7CiAgICAKICAgIG91dCA8LSBjcm9zc2luZyh7e21vZGVsX2RhdGF9fSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoe3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHlfbnVtKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih5X251bSA9PSA4KSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJiLiBhY2YiKQogICAgKQogIH0KICAKICBwZWFrX2RyYXdzIDwtIGFkZF9lcHJlZF9kcmF3cyhuZXdkYXRhID0gb3V0LAogICAgICAgICAgICAgICAgICBvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICBtdXRhdGUoZXByZWRfY25yID0gLmVwcmVkL3BvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAqMTAwMDAwKSAlPiUKICAgIGdyb3VwX2J5KC5kcmF3LCAhIWdyb3VwaW5nX3ZhcikgJT4lCiAgICBzdW1tYXJpc2UoZXN0aW1hdGUgPSBsYXN0KGVwcmVkX2NucikvZmlyc3QoZXByZWRfY25yKSkgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBtdXRhdGUobWVhc3VyZSA9ICJSUi5wZWFrIikKICAKICBwZWFrX3N1bW1hcnkgPC0gcGVha19kcmF3cyAlPiUKICAgIGdyb3VwX2J5KCEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgIG1lYW5fcWkoZXN0aW1hdGUpICU+JQogICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIucGVhayIpCiAgCiAgCiAgI2Z1bmN0aW9ucyBmb3IgY2FsY3VsYXRpbmcgUlIubGV2ZWwKICAjaS5lLiByZWxhdGl2ZSBjYXNlIG5vdGlmaWNhdGlvbiByYXRlIGluIDE5NTggdnMuIGNvdW50ZXJmYWN0dWFsIHRyZW5kIGZvciAxOTU4CiAgCiAgICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIG91dDIgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDkpLAogICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikKICAgICkKICB9IGVsc2UgewogICAgCiAgICBvdXQyIDwtIGNyb3NzaW5nKHt7bW9kZWxfZGF0YX19ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgeV9udW0pICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDkpLAogICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikKICAgICkKICB9CiAgCiAgICBsZXZlbF9kcmF3cyA8LSBhZGRfZXByZWRfZHJhd3MobmV3ZGF0YSA9IG91dDIsCiAgICAgICAgICAgICAgICAgIG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IHt7cmVfZm9ybXVsYX19KSAlPiUKICAgIGFycmFuZ2UoeV9udW0sIC5kcmF3KSAlPiUKICAgIG11dGF0ZShlcHJlZF9jbnIgPSAuZXByZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApICU+JQogICAgZ3JvdXBfYnkoLmRyYXcsICEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3QoZXByZWRfY25yKS9maXJzdChlcHJlZF9jbnIpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLmxldmVsIikKICAKICBsZXZlbF9zdW1tYXJ5IDwtIGxldmVsX2RyYXdzICU+JQogICAgZ3JvdXBfYnkoISFncm91cGluZ192YXIpICU+JQogICAgbWVhbl9xaShlc3RpbWF0ZSkgJT4lCiAgICBtdXRhdGUobWVhc3VyZSA9ICJSUi5sZXZlbCIpCiAgICAKICAgIAogICNmdW5jdGlvbnMgZm9yIGNhbGN1bGF0aW5nIFJSLnNsb3BlCiAgI2kuZS4gcmVsYXRpdmUgY2hhbmdlIGluIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgaW4gMTk1OC0xOTYzIHZzLiBjb3VudGVyZmFjdHVhbCB0cmVuZCBmb3IgMTk1OS0xOTYzCiAgCiAgICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIG91dDMgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtICVpbiUgYyg5LDE0KSksCiAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYy4gcG9zdC1hY2YiKQogICAgKQogIH0gZWxzZSB7CiAgICAKICAgIG91dDMgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoeV9udW0gJWluJSBjKDksMTQpKSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJjLiBwb3N0LWFjZiIpCiAgICApCiAgfQogIAogICAgc2xvcGVfZHJhd3MgPC0gYWRkX2VwcmVkX2RyYXdzKG5ld2RhdGEgPSBvdXQzLAogICAgICAgICAgICAgICAgICBvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICAgICAgYXJyYW5nZSh5X251bSkgJT4lCiAgICAgICAgdW5ncm91cCgpICU+JQogICAgICAgIG11dGF0ZShlcHJlZF9jbnIgPSAuZXByZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApICU+JQogICAgICAgIGdyb3VwX2J5KC5kcmF3LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgIHN1bW1hcmlzZShzbG9wZSA9IGxhc3QoZXByZWRfY25yKS9maXJzdChlcHJlZF9jbnIpKSAlPiUKICAgICAgICB1bmdyb3VwKCkgJT4lCiAgICAgICAgZ3JvdXBfYnkoLmRyYXcsICEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgICAgICBzdW1tYXJpc2UoZXN0aW1hdGUgPSBsYXN0KHNsb3BlKS9maXJzdChzbG9wZSkpICU+JQogICAgICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnNsb3BlIikKICAKICBzbG9wZV9zdW1tYXJ5IDwtIHNsb3BlX2RyYXdzICU+JQogICAgIGdyb3VwX2J5KCEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgICAgbWVhbl9xaShlc3RpbWF0ZSkgJT4lCiAgICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnNsb3BlIikKICAgIAogICNnYXRoZXIgYWxsIHRoZSByZXN1bHRzIGludG8gYSBuYW1lZCBsaXN0CiAgICBsc3QocGVha19kcmF3cz1wZWFrX2RyYXdzLCBwZWFrX3N1bW1hcnk9cGVha19zdW1tYXJ5LCAKICAgICAgICBsZXZlbF9kcmF3cz1sZXZlbF9kcmF3cywgbGV2ZWxfc3VtbWFyeT1sZXZlbF9zdW1tYXJ5LCAKICAgICAgICBzbG9wZV9kcmF3cz1zbG9wZV9kcmF3cywgc2xvcGVfc3VtbWFyeT1zbG9wZV9zdW1tYXJ5KQogIAp9CgpgYGAKCgpGdW5jdGlvbiBmb3IgY2FsY3VsYXRpbmcgZGlmZmVyZW5jZSBmcm9tIGNvdW50ZXJmYWN0dWFsCgpgYGB7cn0KCmNhbGN1bGF0ZV9jb3VudGVyZmFjdHVhbCA8LSBmdW5jdGlvbihtb2RlbF9kYXRhLCBtb2RlbCwgcG9wdWxhdGlvbl9kZW5vbWluYXRvciwgZ3JvdXBpbmdfdmFyPU5VTEwsIHJlX2Zvcm11bGE9TkEpewogIAogICNlZmZlY3QgdnMuIGNvdW50ZXJmYWN0dWFsCiAgY291bnRlcmZhY3QgPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIiksCiAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0ge3tyZV9mb3JtdWxhfX0pICU+JQogICAgICBncm91cF9ieSguZHJhdywgeWVhciwge3tncm91cGluZ192YXJ9fSwgYWNmX3BlcmlvZCkgJT4lCiAgICAgIG11dGF0ZSguZXByZWRfaW5jX2NvdW50ZXJmID0gLmVwcmVkL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCwgLmVwcmVkX2NvdW50ZXJmPS5lcHJlZCkgICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgdW5ncm91cCgpICU+JQogICAgICBzZWxlY3QoeWVhciwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIC5kcmF3LCAuZXByZWRfY291bnRlcmYsIC5lcHJlZF9pbmNfY291bnRlcmYsIHt7Z3JvdXBpbmdfdmFyfX0pCiAgCiAgI0NhbGN1YXRlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgcGVyIGRyYXcsIHRoZW4gc3VtbWFyaXNlLgogIHBvc3RfY2hhbmdlIDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0ge3ttb2RlbF9kYXRhfX0gJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5ZWFyLCB5ZWFyMiwgeV9udW0sIHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB7e2dyb3VwaW5nX3Zhcn19LCBhY2ZfcGVyaW9kKSwKICAgICAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICAgIGdyb3VwX2J5KC5kcmF3LCB5ZWFyLCB7e2dyb3VwaW5nX3Zhcn19LCBhY2ZfcGVyaW9kKSAlPiUKICAgICAgbXV0YXRlKC5lcHJlZF9pbmMgPSAuZXByZWQve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwKSAgJT4lCiAgICAgIGZpbHRlcih5ZWFyPjE5NTcpICU+JQogICAgICB1bmdyb3VwKCkgJT4lCiAgICAgIHNlbGVjdCh5ZWFyLCB7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwge3tncm91cGluZ192YXJ9fSwgLmRyYXcsIC5lcHJlZCwgLmVwcmVkX2luYywge3tncm91cGluZ192YXJ9fSkgCiAgCiAgI2ZvciB0aGUgb3ZlcmFsbCBwZXJpb2QKICAgIGNvdW50ZXJmYWN0X292ZXJhbGwgPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIiksCiAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0ge3tyZV9mb3JtdWxhfX0pICU+JQogICAgICBncm91cF9ieSguZHJhdywge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgIGZpbHRlcih5ZWFyPjE5NTcpICU+JQogICAgICB1bmdyb3VwKCkgJT4lCiAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgLmRyYXcsIC5lcHJlZCwge3tncm91cGluZ192YXJ9fSkgICU+JQogICAgICBncm91cF9ieSguZHJhdywge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWRfY291bnRlcmYgPSBzdW0oLmVwcmVkKSkgCiAgCiAgI0NhbGN1YXRlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgcGVyIGRyYXcsIHRoZW4gc3VtbWFyaXNlLgogIHBvc3RfY2hhbmdlX292ZXJhbGwgPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0sIGFjZl9wZXJpb2QpLAogICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IHt7cmVfZm9ybXVsYX19KSAlPiUKICAgICAgZ3JvdXBfYnkoLmRyYXcsIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgdW5ncm91cCgpICU+JQogICAgICBzZWxlY3Qoe3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0sIC5kcmF3LCAuZXByZWQpICU+JQogICAgICBncm91cF9ieSguZHJhdywge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWQgPSBzdW0oLmVwcmVkKSkgCiAgCiAgCmNvdW50ZXJfcG9zdCA8LQogIGxlZnRfam9pbihjb3VudGVyZmFjdCwgcG9zdF9jaGFuZ2UpICU+JQogICAgbXV0YXRlKGNhc2VzX2F2ZXJ0ZWQgPSAuZXByZWRfY291bnRlcmYtLmVwcmVkLAogICAgICAgICAgIHBjdF9jaGFuZ2UgPSAoLmVwcmVkIC0gLmVwcmVkX2NvdW50ZXJmKS8uZXByZWRfY291bnRlcmYsCiAgICAgICAgICAgZGlmZl9pbmMxMDBrID0gLmVwcmVkX2luYyAtIC5lcHJlZF9pbmNfY291bnRlcmYsCiAgICAgICAgICAgcnJfaW5jMTAwayA9IC5lcHJlZF9pbmMvLmVwcmVkX2luY19jb3VudGVyZikgJT4lCiAgICBncm91cF9ieSh5ZWFyLCB7e2dyb3VwaW5nX3Zhcn19KSAlPiUKICAgIG1lYW5fcWkoY2FzZXNfYXZlcnRlZCwgcGN0X2NoYW5nZSwgZGlmZl9pbmMxMDBrLCBycl9pbmMxMDBrKSAlPiUKICAgIHVuZ3JvdXAoKQoKY291bnRlcl9wb3N0X292ZXJhbGwgPC0KICBsZWZ0X2pvaW4oY291bnRlcmZhY3Rfb3ZlcmFsbCwgcG9zdF9jaGFuZ2Vfb3ZlcmFsbCkgJT4lCiAgICBtdXRhdGUoY2FzZXNfYXZlcnRlZCA9IC5lcHJlZF9jb3VudGVyZi0uZXByZWQsCiAgICAgICAgICAgcGN0X2NoYW5nZSA9ICguZXByZWQgLSAuZXByZWRfY291bnRlcmYpLy5lcHJlZF9jb3VudGVyZikgJT4lCiAgICBncm91cF9ieSh7e2dyb3VwaW5nX3Zhcn19KSAlPiUKICAgIG1lYW5fcWkoY2FzZXNfYXZlcnRlZCwgcGN0X2NoYW5nZSkgJT4lCiAgICB1bmdyb3VwKCkKCmxzdChjb3VudGVyX3Bvc3QsIGNvdW50ZXJfcG9zdF9vdmVyYWxsKQoKfQoKCmBgYAoKRnVuY3Rpb24gZm9yIHRpZHlpbmcgdXAgY291bnRlcmZhY3R1YWxzIChtb3N0bHkgZm9yIG1ha2luZyBuaWNlIHRhYmxlcykKCmBgYHtyfQoKdGlkeV9jb3VudGVyZmFjdHVhbHMgPC0gZnVuY3Rpb24oZGF0YSl7CiAgZGF0YSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyLCBkaWZmX2luYzEwMGs6ZGlmZl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHJyX2luYzEwMGs6cnJfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjAxKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUoeWVhciA9IGFzLmNoYXJhY3Rlcih5ZWFyKSwKICAgICAgICAgICAgY2FzZXNfYXZlcnRlZCA9IGdsdWU6OmdsdWUoIntjYXNlc19hdmVydGVkfSAoe2Nhc2VzX2F2ZXJ0ZWQubG93ZXJ9IHRvIHtjYXNlc19hdmVydGVkLnVwcGVyfSkiKSwKICAgICAgICAgICAgcGN0X2NoYW5nZSA9IGdsdWU6OmdsdWUoIntwY3RfY2hhbmdlfSAoe3BjdF9jaGFuZ2UubG93ZXJ9IHRvIHtwY3RfY2hhbmdlLnVwcGVyfSkiKSwKICAgICAgICAgICAgZGlmZl9pbmMgPSBnbHVlOjpnbHVlKCJ7ZGlmZl9pbmMxMDBrfSAoe2RpZmZfaW5jMTAway5sb3dlcn0gdG8ge2RpZmZfaW5jMTAway51cHBlcn0pIiksCiAgICAgICAgICAgIHJyX2luYyA9IGdsdWU6OmdsdWUoIntycl9pbmMxMDBrfSAoe3JyX2luYzEwMGsubG93ZXJ9IHRvIHtycl9pbmMxMDBrLnVwcGVyfSkiKSkKfQoKCnRpZHlfY291bnRlcmZhY3R1YWxzX292ZXJhbGwgPC0gZnVuY3Rpb24oZGF0YSl7CiAgZGF0YSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZSh5ZWFyID0gYXMuY2hhcmFjdGVyKHllYXIpLAogICAgICAgICAgICBjYXNlc19hdmVydGVkID0gZ2x1ZTo6Z2x1ZSgie2Nhc2VzX2F2ZXJ0ZWR9ICh7Y2FzZXNfYXZlcnRlZC5sb3dlcn0gdG8ge2Nhc2VzX2F2ZXJ0ZWQudXBwZXJ9KSIpLAogICAgICAgICAgICBwY3RfY2hhbmdlID0gZ2x1ZTo6Z2x1ZSgie3BjdF9jaGFuZ2V9ICh7cGN0X2NoYW5nZS5sb3dlcn0gdG8ge3BjdF9jaGFuZ2UudXBwZXJ9KSIpKQp9CgpgYGAKCgoKIyMjIDIuIERhdGEKCkltcG9ydCBkYXRhc2V0cyBmb3IgYW5hbHlzaXMKCiMjIyMgMi4xIFNoYXBlZmlsZXMKCk1ha2UgYSBtYXAgb2YgR2xhc2dvdyB3YXJkcwoKYGBge3J9CgpnbGFzZ293X3dhcmRzXzE5NTEgPC0gc3RfcmVhZChoZXJlKCJtYXBwaW5nL2dsYXNnb3dfd2FyZHNfMTk1MS5nZW9qc29uIikpCgpgYGAKCmBgYHtyfQoKI3JlYWQgaW4gU2NvdGxhbmQgYm91bmRhcnkKc2NvdGxhbmQgPC0gc3RfcmVhZChoZXJlKCJtYXBwaW5nL1Njb3RsYW5kX2JvdW5kYXJ5L1Njb3RsYW5kIGJvdW5kYXJ5LnNocCIpKQoKI21ha2UgYSBib3VuZGluZyBib3ggZm9yIEdsYXNnb3cKYmJveCA8LSBzdF9iYm94KGdsYXNnb3dfd2FyZHNfMTk1MSkgfD4gc3RfYXNfc2ZjKCkKCiNwbG90IHNjb3RsYW5kIHdpdGggYSBib3VuZGluZyBib3ggYXJvdW5kIHRoZSBDaXR5IG9mIEdsYXNnb3cKc2NvdGxhbmRfd2l0aF9iYm94IDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBzY290bGFuZCwgZmlsbD0iYW50aXF1ZXdoaXRlIikgKwogIGdlb21fc2YoZGF0YSA9IGJib3gsIGNvbG91ciA9ICIjQzYwQzMwIiwgZmlsbD0iYW50aXF1ZXdoaXRlIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BLCBsaW5ld2lkdGggPSAwLjUpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjRUFGN0ZBIiwgc2l6ZSA9IDAuMykpCgojcGxvdCB0aGUgd2FyZHMKI25vdGUgd2UgdGlkeSB1cCBzb21lIG5hbWVzIHRvIGZpdCBvbiBtYXAKZ2xhc2dvd193YXJkX21hcCA8LSBnbGFzZ293X3dhcmRzXzE5NTEgJT4lCiAgbXV0YXRlKHdhcmQgPSBjYXNlX3doZW4od2FyZD09IlNoZXR0bGVzdG9uIGFuZCBUb2xsY3Jvc3MiIH4gIlNoZXR0bGVzdG9uIGFuZFxuVG9sbGNyb3NzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICB3YXJkPT0iUGFydGljayAoV2VzdCkiIH4gIlBhcnRpY2tcbihXZXN0KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2FyZD09IlBhcnRpY2sgKEVhc3QpIiB+ICJQYXJ0aWNrXG4oRWFzdCkiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHdhcmQ9PSJOb3J0aCBLZWx2aW4iIH4gIk5vcnRoXG5LZWx2aW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgIHdhcmQ9PSJLaW5uaW5nIFBhcmsiIH4gIktpbm5pbmdcblBhcmsiLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB3YXJkKSkgJT4lCiAgCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGw9ZGl2aXNpb24pKSArCiAgZ2VvbV9zZl9sYWJlbChhZXMobGFiZWwgPSB3YXJkKSwgc2l6ZT0zLCBmaWxsPU5BLCBsYWJlbC5zaXplID0gTkEsIGNvbG91cj0iYmxhY2siKSArCiAgI3NjYWxlX2NvbG91cl9pZGVudGl0eSgpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiLCBuYW1lPSJDaXR5IG9mIEdsYXNnb3cgRGl2aXNpb24iKSArCiAgdGhlbWVfZ3JleSgpICsKICBsYWJzKHg9IiIsCiAgICAgICB5PSIiLAogICAgICAgZmlsbD0iRGl2aXNpb24iKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BLCBsaW5ld2lkdGggPSAwLjUpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhbnRpcXVld2hpdGUiLCBzaXplID0gMC4zKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyZXk3OCIpKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIHRpdGxlLmhqdXN0ID0gMC41LCB0aXRsZS50aGVtZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpKQoKI2FkZCB0aGUgbWFwIG9mIHNjb3RsYW5kIGFzIGFuIGluc2V0CmdsYXNnb3dfd2FyZF9tYXAgKyBpbnNldF9lbGVtZW50KHNjb3RsYW5kX3dpdGhfYmJveCwgMC43NSwgMCwgMSwgMC40KQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczEudGlmZiIpLCBoZWlnaHQ9MTAsIHdpZHRoID0gMTIpCgoKYGBgCgojIyMgMy4gRGVub21pbmF0b3JzCgpMb2FkIGluIHRoZSBkYXRhc2V0cyBmb3IgZGVub25vbWlhdG9ycywgYW5kIGNoZWNrIGZvciBjb25zaXN0ZW5jeS4KCmBgYHtyfQoKb3ZlcmFsbF9wb3BzIDwtIHJlYWRfeGxzeChwYXRoID0gIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gIm92ZXJhbGxfcG9wdWxhdGlvbiIpCgpvdmVyYWxsX3BvcHMgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSAmICEoeWVhciksICB+Y29tbWEoLikpKSAlPiUKICBkYXRhdGFibGUoKQoKI3NoaWZ0IHllYXIgdG8gbWlkcG9pbnQKb3ZlcmFsbF9wb3BzIDwtIG92ZXJhbGxfcG9wcyAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkKCmBgYAoKTm90ZSwgd2UgaGF2ZSB0aHJlZSBwb3B1bGF0aW9uIGVzdGltYXRlczoKCjEuIFBvcHVsYXRpb24gd2l0aG91dCBpbnN0aXR1dGlvbmFsaXNlZCBwZW9wbGUgb3IgcGVvcGxlIGluIHNoaXBwaW5nCjIuIFBvcHVsYXRpb24gaW4gaW5zdGl0dXRpb25zCjMuIFBvcHVsYXRpb24gaW4gc2hpcHBpbmcKCihQb3B1bGF0aW9uIGluIHNoaXBwaW5nIGlzIGVzdGltYXRlZCBmcm9tIHRoZSAxOTUxIGNlbnN1cywgc28gaXMgdGhlIHNhbWUgZm9yIG1vc3QgeWVhcnMpCgojIyMjIDMuMSBPdmVyYWxsIHBvcHVsYXRpb24KCkZpcnN0LCBwbG90IHRoZSB0b3RhbCBwb3B1bGF0aW9uCgpgYGB7cn0KCm92ZXJhbGxfcG9wcyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PXRvdGFsX3BvcHVsYXRpb24sIHg9eWVhcjIpLCBhbHBoYT0wLjUsIGNvbG91ciA9ICJtZWRpdW1zZWFncmVlbiIsIGZpbGw9Im1lZGl1bXNlYWdyZWVuIikgKwogIGdlb21fcG9pbnQoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiksIGNvbG91ciA9ICJtZWRpdW1zZWFncmVlbiIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMpICsKICBsYWJzKAogICAgdGl0bGUgPSAiR2xhc2dvdyBDb3Jwb3JhdGlvbjogdG90YWwgcG9wdWxhdGlvbiIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMiLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiUG9wdWxhdGlvbiIsCiAgICBjYXB0aW9uID0gIk1pZC15ZWFyIGVzdGltYXRlc1xuTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkKCgpgYGAKCk5vdyB0aGUgcG9wdWxhdGlvbiBleGNsdWRpbmcgaW5zdGl0dXRpb25hbGlzZWQgYW5kIHNoaXBwaW5nIHBvcHVsYXRpb24KCmBgYHtyfQoKb3ZlcmFsbF9wb3BzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9cG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgeD15ZWFyMiksIGFscGhhPTAuNSwgY29sb3VyID0gInB1cnBsZSIsIGZpbGw9InB1cnBsZSIpICsKICBnZW9tX3BvaW50KGFlcyh5PXBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIHg9eWVhcjIpLCBjb2xvdXIgPSAicHVycGxlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBwb3B1bGF0aW9uIGV4Y2x1ZGluZyBpbnN0aXR1dGlvbmFsaXNlZCBhbmQgc2hpcHBpbmciLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIlBvcHVsYXRpb24iLAogICAgY2FwdGlvbiA9ICJNaWQteWVhciBlc3RpbWF0ZXNcbk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lX2dnZGlzdCgpCgoKYGBgCgojIyMjIDMuMiBQb3B1bGF0aW9uIGJ5IFdhcmQKClRoZXJlIGFyZSA1IERpdmlzaW9ucyBjb250YWluaW5nIDM3IFdhcmRzIGluIHRoZSBHbGFzZ293IENvcnBvcmF0aW9uLCB3aXRoIGNvbnNpc3RlbnQgYm91bmRhcmllcyBvdmVyIHRpbWUuCgpgYGB7cn0KI2xvb2stdXAgdGFibGUgZm9yIGRpdmlzaW9ucyBhbmQgd2FyZHMKd2FyZF9sb29rdXAgPC0gcmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4Iiwgc2hlZXQgPSAiZGl2aXNpb25zX3dhcmRzIikKCgp3YXJkX3BvcHMgPC0gcmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4Iiwgc2hlZXQgPSAid2FyZF9wb3B1bGF0aW9uIikKCndhcmRfcG9wcyAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpICYgISh5ZWFyKSwgIH5jb21tYSguKSkpICU+JQogIGRhdGF0YWJsZSgpCgojc2hpZnQgeWVhciB0byBtaWRwb2ludAp3YXJkX3BvcHMgPC0gd2FyZF9wb3BzICU+JQogIG11dGF0ZSh5ZWFyMiA9IHllYXIrMC41KQoKI0dldCB0aGUgRGl2aXNpb24gcG9wdWxhdGlvbgpkaXZpc2lvbl9wb3BzIDwtIHdhcmRfcG9wcyAlPiUKICBncm91cF9ieShkaXZpc2lvbiwgeWVhcikgJT4lCiAgc3VtbWFyaXNlKHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAgPSBzdW0ocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgaW5zdGl0dXRpb25zID0gc3VtKGluc3RpdHV0aW9ucywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgc2hpcHBpbmcgPSBzdW0oc2hpcHBpbmcsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHRvdGFsX3BvcHVsYXRpb24gPSBzdW0odG90YWxfcG9wdWxhdGlvbiwgbmEucm0gPSBUUlVFKSkKCmRpdmlzaW9uX3BvcHMgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSAmICEoeWVhciksICB+Y29tbWEoLikpKSAlPiUKICBkYXRhdGFibGUoKQoKYGBgCgpQbG90IHRoZSBvdmVyYWxsIHBvcHVsYXRpb24gYnkgRGl2aXNpb24gYW5kIFdhcmQKCmBgYHtyfQoKZGl2aXNpb25fcG9wcyAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYXJlYShhZXMoeT10b3RhbF9wb3B1bGF0aW9uLCB4PXllYXIyLCBjb2xvdXI9ZGl2aXNpb24sIGZpbGw9ZGl2aXNpb24pLCBhbHBoYT0wLjgpICsKICBnZW9tX3BvaW50KGFlcyh5PXRvdGFsX3BvcHVsYXRpb24sIHg9eWVhcjIsIGNvbG91cj1kaXZpc2lvbikpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIGZhY2V0X3dyYXAoZGl2aXNpb25+LikgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfYXhpcyhhbmdsZSA9IDkwKSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIsIG5hbWUgPSAiIikgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIiwgbmFtZSA9ICIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IHRvdGFsIHBvcHVsYXRpb24gYnkgRGl2aXNpb24iLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIlBvcHVsYXRpb24iLAogICAgY2FwdGlvbiA9ICJNaWQteWVhciBlc3RpbWF0ZXNcbk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCgpgYGAKCmBgYHtyfQoKd2FyZF9wb3BzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiwgY29sb3VyPWRpdmlzaW9uLCBmaWxsPWRpdmlzaW9uKSkgKwogIGdlb21fcG9pbnQoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiwgY29sb3VyPWRpdmlzaW9uKSwgY29sb3VyPSJibGFjayIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIGZhY2V0X3dyYXAod2FyZH4uLCBuY29sPTYpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2F4aXMoYW5nbGUgPSA5MCkpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiLCBuYW1lPSJEaXZpc2lvbiIpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIsIG5hbWUgPSAiRGl2aXNpb24iKSArCiAgbGFicygKICAgIHggPSAiIiwKICAgIHkgPSAiUG9wdWxhdGlvbiIsCiAgICBjYXB0aW9uID0gIk1pZC15ZWFyIGVzdGltYXRlc1xuTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczMudGlmZiIpLCBoZWlnaHQ9MTQsIHdpZHRoPTEyKQoKYGBgCgpBcHByb3hpbWF0ZWx5LCBob3cgbWFueSBwZXJzb24teWVhcnMgb2YgZm9sbG93LXVwIGRvIHdlIGhhdmU/CgpgYGB7cn0KCm92ZXJhbGxfcG9wcyAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3VtbWFyaXNlKGFjcm9zcyh5ZWFyLCBsZW5ndGgsIC5uYW1lcyA9ICJ5ZWFycyIpLAogICAgICAgICAgICBhY3Jvc3MoYyhwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCB0b3RhbF9wb3B1bGF0aW9uKSwgc3VtKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5kb3VibGUpLCBjb21tYSkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpDaGFuZ2UgaW4gcG9wdWxhdGlvbiBieSB3YXJkCgpgYGB7cn0KCndhcmRfcG9wcyAlPiUKICBncm91cF9ieSh3YXJkKSAlPiUKICBzdW1tYXJpc2UocGN0X2NoYW5nZV9wb3AgPSAobGFzdChwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSAtIGZpcnN0KHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXApKS9maXJzdChwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSkgJT4lCiAgbXV0YXRlKHBjdF9jaGFuZ2VfcG9wID0gcGVyY2VudChwY3RfY2hhbmdlX3BvcCkpICU+JQogIGFycmFuZ2UocGN0X2NoYW5nZV9wb3ApICU+JQogIGRhdGF0YWJsZSgpCiAgCgoKYGBgCgoKIyMjIyAzLjMgUG9wdWxhdGlvbiBieSBhZ2UgYW5kIHNleAoKYGBge3J9CgphZ2Vfc2V4IDwtIHJlYWRfeGxzeChwYXRoID0gIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gImFnZV9zZXhfcG9wdWxhdGlvbiIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhtYWxlLCBmZW1hbGUpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJzZXgiKQoKI2NvbGxhcHNlIGRvd24gdG8gc21hbGxlciBhZ2UgZ3JvdXBzIHRvIGJlIG1hbmFnZWFibGUKYWdlX3NleCA8LSBhZ2Vfc2V4ICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZSA9PSAiMCB0byA0IiB+ICIwMCB0byAwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2UgPT0gIjUgdG8gOSIgfiAiMDUgdG8gMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIxMCB0byAxNCIgfiAiMDUgdG8gMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIxNSB0byAxOSIgfiAiMTUgdG8gMjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIyMCB0byAyNCIgfiAiMTUgdG8gMjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIyNSB0byAyOSIgfiAiMjUgdG8gMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIzMCB0byAzNCIgfiAiMjUgdG8gMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIzNSB0byAzOSIgfiAiMzUgdG8gNDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI0MCB0byA0NCIgfiAiMzUgdG8gNDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI0NSB0byA0OSIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI1MCB0byA1NCIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI1NSB0byA1OSIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICI2MCAmIHVwIikpICU+JQogIGdyb3VwX2J5KHllYXIsIGFnZSwgc2V4KSAlPiUKICBtdXRhdGUodmFsdWUgPSBzdW0odmFsdWUpKSAlPiUKICB1bmdyb3VwKCkKCgoKbV9hZ2Vfc2V4IDwtIGxtKHZhbHVlIH4gc3BsaW5lczo6bnMoeWVhciwga25vdHMgPSAzKSphZ2Uqc2V4LCBkYXRhID0gYWdlX3NleCkKCnN1bW1hcnkobV9hZ2Vfc2V4KQoKYWdlX2xldmVscyA8LSBhZ2Vfc2V4ICU+JSBzZWxlY3QoYWdlKSAlPiUgZGlzdGluY3QoKSAlPiUgcHVsbCgpIAoKYWdlX3NleF9uZCA8LSAKICBjcm9zc2luZygKICAgIGFnZT1hZ2VfbGV2ZWxzLAogICAgc2V4PWMoIm1hbGUiLCAiZmVtYWxlIiksCiAgICB5ZWFyID0gMTk1MDoxOTYzCiAgKQoKcHJlZF9wb3BzIDwtIGFnZV9zZXhfbmQgJT4lIG1vZGVscjo6YWRkX3ByZWRpY3Rpb25zKG1fYWdlX3NleCkKCnByZWRfcG9wcyAlPiUKICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT1wcmVkLCBjb2xvdXI9YWdlKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X2dyaWQoc2V4fi4pICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEsIGxpbWl0cyA9IGMoMCwgMTI1MDAwKSkKCiNIb3cgd2VsbCBkbyB0aGV5IG1hdGNoIHVwIHdpdGggb3VyIG92ZXJhbGwgcG9wdWxhdGlvbnM/CnByZWRfcG9wcyAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpc2Uoc3VtX3ByZWRfcG9wID0gc3VtKHByZWQpKSAlPiUKICByaWdodF9qb2luKG92ZXJhbGxfcG9wcykgJT4lCiAgc2VsZWN0KHllYXIsIHN1bV9wcmVkX3BvcCwgcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgdG90YWxfcG9wdWxhdGlvbikgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKHN1bV9wcmVkX3BvcCwgcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgdG90YWxfcG9wdWxhdGlvbikpICU+JQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXZhbHVlLCBjb2xvdXI9bmFtZSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSwgbGltaXRzID0gYyg4MDAwMDAsIDEyNTAwMDApKQoKcHJlZF9wb3BzICU+JQogIGdyb3VwX2J5KHllYXIsIHNleCkgJT4lCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShwcmVkKSkgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgbXV0YXRlKHNleF9yYXRpbyA9IGZpcnN0KHN1bSkvbGFzdChzdW0pKQpgYGAKCgoKCgpQb3B1bGF0aW9uIHB5cmFtaWRzCgpgYGB7cn0KCmxhYmVsX2FicyA8LSBmdW5jdGlvbih4KSB7CiAgY29tbWEoYWJzKHgpKQp9CgoKcHJlZF9wb3BzICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBtdXRhdGUoeWVhcl9wb3AgPSBzdW0ocHJlZCksCiAgICAgICAgIGFnZV9zZXhfcGN0ID0gcGVyY2VudChwcmVkL3llYXJfcG9wLCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Im1hbGUiIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iZmVtYWxlIiB+ICJGZW1hbGUiKSkgJT4lCiAgZ2dwbG90KAogICAgYWVzKHggPSBhZ2UsIGZpbGwgPSBzZXgsIAogICAgICAgIHkgPSBpZmVsc2UodGVzdCA9IHNleCA9PSAiRmVtYWxlIix5ZXMgPSAtcHJlZCwgbm8gPSBwcmVkKSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gYWdlX3NleF9wY3QpLAogICAgICAgICAgICBwb3NpdGlvbj0gcG9zaXRpb25fc3RhY2sodmp1c3Q9MC41KSwgY29sb3VyPSJibGFjayIsIHNpemU9Mi41KSArCiAgZmFjZXRfd3JhcCh5ZWFyfi4sIG5jb2w9NykgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX2FicykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNDRDdBQzUiLCAiY2FkZXRibHVlMyIpLCBuYW1lPSIiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdCA9IDEsIHZqdXN0PTAuNSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iIiwgeT0iIikgCgoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczQudGlmZiIpLCB3aWR0aD0xMCkKCgpgYGAKCk5vdCBwZXJmZWN0LCBidXQgcmVzb25hYmx5IGdvb2QuIEJ1dCBhaGhoaGguLi4gdGhlIGFnZSBncm91cHMgZG9uJ3QgYWxpZ24gd2l0aCB0aGUgY2FzZSBub3RpZmljYXRpb24gYWdlIGdyb3VwcyEgQ29tZSBiYWNrIHRvIHRoaW5rIGFib3V0IHRoaXMgbGF0ZXIuCgoKIyMjIDQuIFR1YmVyY3Vsb3NpcyBjYXNlcwoKSW1wb3J0IHRoZSB0dWJlcmN1bG9zaXMgY2FzZXMgZGF0YXNldAoKCiMjIyMgNC4xIE92ZXJhbGwgbm90aWZpY2F0aW9ucwoKT3ZlcmFsbCwgYnkgeWVhci4KCmBgYHtyfQoKY2FzZXNfYnlfeWVhciA8LSByZWFkX3hsc3goIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gImJ5X3llYXIiKQoKY2FzZXNfYnlfeWVhciU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgojc2hpZnQgeWVhciB0byBtaWRwb2ludApjYXNlc19ieV95ZWFyIDwtIGNhc2VzX2J5X3llYXIgJT4lCiAgbXV0YXRlKHllYXIyID0geWVhciswLjUpCgpgYGAKClBsb3QgdGhlIG92ZXJhbGwgbnVtYmVyIG9mIGNhc2Ugbm90aWZpZWQgcGVyIHllYXIsIGJ5IHB1bG1vbmFyeSBhbmQgZXh0cmEgcHVsbW9uYXJ5IGNsYXNzaWZpY2F0aW9uLgoKYGBge3J9CgpjYXNlc19ieV95ZWFyICU+JQogIHNlbGVjdCgtdG90YWxfbm90aWZpY2F0aW9ucywgLXllYXIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhwdWxtb25hcnlfbm90aWZpY2F0aW9ucywgYG5vbi1wdWxtb25hcnlfbm90aWZpY2F0aW9uc2ApKSAlPiUKICBtdXRhdGUobmFtZSA9IGNhc2Vfd2hlbihuYW1lID09ICJwdWxtb25hcnlfbm90aWZpY2F0aW9ucyIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID09ICJub24tcHVsbW9uYXJ5X25vdGlmaWNhdGlvbnMiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PXZhbHVlLCB4PXllYXIyLCBncm91cCA9IG5hbWUsIGZpbGw9bmFtZSksIGFscGhhPTAuNSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscykgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIsIG5hbWU9IiIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiR2xhc2dvdyBDb3Jwb3JhdGlvbjogVHViZXJjdWxvc2lzIG5vdGlmaWNhdGlvbnMiLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzLCBieSBUQiBkaXNlYXNlIGNsYXNzaWZpY2F0aW9uIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIk51bWJlciBvZiBjYXNlcyIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICAKCmBgYAoKIyMjIyA0LjIgTm90aWZpY2F0aW9ucyBieSBEaXZpc2lvbgoKUmVhZCBpbiB0aGUgZGF0YXNldHMgYW5kIG1lcmdlIHRvZ2V0aGVyLgoKYGBge3J9CgojbGlzdCBhbGwgdGhlIHNoZWV0cwphbGxfc2hlZXRzIDwtIGV4Y2VsX3NoZWV0cygiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4IikKCiNnZXQgdGhlIHdhcmQgc2hlZXRzCndhcmRfc2hlZXRzIDwtIGVuZnJhbWUoYWxsX3NoZWV0cykgJT4lCiAgZmlsdGVyKGdyZXBsKCJieV93YXJkIiwgdmFsdWUpKSAlPiUKICBwdWxsKHZhbHVlKQoKCmNhc2VzX2J5X3dhcmRfc2V4X3llYXIgPC0gbWFwX2RmKHdhcmRfc2hlZXRzLCB+cmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9IC4pKQoKY2FzZXNfYnlfd2FyZF9zZXhfeWVhciAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpICYgISh5ZWFyKSwgIH5jb21tYSguKSkpICU+JQogIGRhdGF0YWJsZSgpCgpgYGAKCkFnZ3JlZ2F0ZSB0b2dldGhlciB0byBnZXQgY2FzZXMgYnkgZGl2aXNpb24KCmBgYHtyfQoKY2FzZXNfYnlfZGl2aXNpb24gPC0gY2FzZXNfYnlfd2FyZF9zZXhfeWVhciAlPiUKICBsZWZ0X2pvaW4od2FyZF9sb29rdXApICU+JQogIGdyb3VwX2J5KGRpdmlzaW9uLCB5ZWFyLCB0Yl90eXBlKSAlPiUKICBzdW1tYXJpc2UoY2FzZXMgPSBzdW0oY2FzZXMsIG5hLnJtID0gVFJVRSkpCgojc2hpZnQgeWVhciB0byBtaWRwb2ludApjYXNlc19ieV9kaXZpc2lvbiA8LSBjYXNlc19ieV9kaXZpc2lvbiAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkgJT4lCiAgdW5ncm91cCgpCgpjYXNlc19ieV9kaXZpc2lvbiAgJT4lCiAgc2VsZWN0KC15ZWFyMikgJT4lCiAgc2VsZWN0KHllYXIsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSAmICEoeWVhciksICB+Y29tbWEoLikpKSAlPiUKICBkYXRhdGFibGUoKQoKCmNhc2VzX2J5X2RpdmlzaW9uICU+JQogIG11dGF0ZSh0Yl90eXBlID0gY2FzZV93aGVuKHRiX3R5cGUgPT0gIlB1bG1vbmFyeSIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICB0Yl90eXBlID09ICJOb24tUHVsbW9uYXJ5IiB+ICJFeHRyYS1wdWxtb25hcnkgVEIiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYXJlYShhZXMoeT1jYXNlcywgeD15ZWFyMiwgZ3JvdXAgPSB0Yl90eXBlLCBmaWxsPXRiX3R5cGUpLCBhbHBoYT0wLjUpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfYXhpcyhhbmdsZSA9IDkwKSkgKwogIGZhY2V0X3dyYXAoZGl2aXNpb25+Liwgc2NhbGVzID0gImZyZWVfeSIpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBuYW1lPSIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IFR1YmVyY3Vsb3NpcyBub3RpZmljYXRpb25zIGJ5IERpdmlzaW9uIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJOdW1iZXIgb2YgY2FzZXMiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KVxuTm90ZTogZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGJ5IERpdmlzaW9uL1dhcmQgbm90IHJlcG9ydGVkIGluIDE5NjItMTk2MyIKICApICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpgYGAKCiMjIyMgNC4zIE5vdGlmaWNhdGlvbnMgYnkgd2FyZAoKYGBge3J9CgoKY2FzZXNfYnlfd2FyZCA8LSBjYXNlc19ieV93YXJkX3NleF95ZWFyICU+JQogIGdyb3VwX2J5KHdhcmQsIHllYXIsIHRiX3R5cGUpICU+JQogIHN1bW1hcmlzZShjYXNlcyA9IHN1bShjYXNlcywgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCgpjYXNlc19ieV93YXJkICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgc2VsZWN0KHllYXIsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgZGF0YXRhYmxlKCkKCiNzaGlmdCB5ZWFyIHRvIG1pZHBvaW50CmNhc2VzX2J5X3dhcmQgPC0gY2FzZXNfYnlfd2FyZCAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkKCmNhc2VzX2J5X3dhcmQgJT4lCiAgbXV0YXRlKHRiX3R5cGUgPSBjYXNlX3doZW4odGJfdHlwZSA9PSAiUHVsbW9uYXJ5IiB+ICJQdWxtb25hcnkgVEIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRiX3R5cGUgPT0gIk5vbi1QdWxtb25hcnkiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PWNhc2VzLCB4PXllYXIyLCBncm91cCA9IHRiX3R5cGUsIGZpbGw9dGJfdHlwZSksIGFscGhhPTAuOCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgZmFjZXRfd3JhcCh3YXJkfi4sIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbmFtZT0iIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBUdWJlcmN1bG9zaXMgbm90aWZpY2F0aW9ucyBieSBXYXJkIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJOdW1iZXIgb2YgY2FzZXMiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KVxuTm90ZTogZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGJ5IERpdmlzaW9uL1dhcmQgbm90IHJlcG9ydGVkIGluIDE5NjItMTk2MyIKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCgpgYGAKCiMjIyMgNC40IE5vdGlmaWNhdGlvbnMgYnkgYWdlIGFuZCBzZXgKCkFzIHdlIGRvbid0IGhhdmUgZGVub21pbmF0b3JzLCB3ZSB3aWxsIGp1c3QgbW9kZWwgdGhlIGNoYW5nZSBpbiBjb3VudHMuCgpgYGB7cn0KCiNsaXN0IGFsbCB0aGUgc2hlZXRzCmFsbF9zaGVldHMgPC0gZXhjZWxfc2hlZXRzKCIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giKQoKI2dldCB0aGUgd2FyZCBzaGVldHMKYWdlX3NleF9zaGVldHMgPC0gZW5mcmFtZShhbGxfc2hlZXRzKSAlPiUKICBmaWx0ZXIoZ3JlcGwoImJ5X2FnZV9zZXgiLCB2YWx1ZSkpICU+JQogIHB1bGwodmFsdWUpCgoKY2FzZXNfYnlfYWdlX3NleCA8LSBtYXBfZGYoYWdlX3NleF9zaGVldHMsIH5yZWFkX3hsc3gocGF0aCA9ICIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gLikpCgpjYXNlc19ieV9hZ2Vfc2V4ICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgpgYGAKCiMjIyA0LjUgVXB0YWtlIG9mIHNjcmVlbmluZwoKV2hhdCBwZXJjZW50YWdlIG9mIGFkdWx0cyAoMTUrIHBhcnRpY2lwYXRlZCBpbiB0aGUgaW50ZXJ2ZW50aW9uIGluIDE5NTcpPwoKTm90ZSB0aGF0IGluIHRoZSBSZXBvcnQgb2YgU2lyIEtlbm5ldGggQ293YW4sIHdlIGhhdmUgdGhlIGZvbGxvd2luZyBlc3RpbWF0ZXMgb2YgcGFydGljaXBhdGlvbiAod2Ugd2lsbCB1c2UgdGhlc2UgZm9yIHRoZSBtYW51c2NyaXB0LCBhcyB0aGV5IGFyZSBub3QgYmFzZWQgb24gbXkgZXN0aW1hdGVzKQoKYGBge3J9Cm1hbGVfYWR1bHRfcmVzaWRlbnRfcGFydGljaXBhdGlvbiA8LSAyODE4NzUKZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BhcnRpY2lwYXRpb24gPC0gMzQwNDc0Cm1hbGVfYWR1bHRfcmVzaWRlbnRfcG9wdWxhdGlvbiA8LSAzODE3MTMKZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24gPC0gNDM3NTg4Cgojb3ZlcmFsbCBwYXJ0aWNpcGF0aW9uCihtYWxlX2FkdWx0X3Jlc2lkZW50X3BhcnRpY2lwYXRpb24rZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BhcnRpY2lwYXRpb24pLyhtYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24rZmVtYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24pCgojbWFsZSBwYXJ0aWNpcGF0aW9uCm1hbGVfYWR1bHRfcmVzaWRlbnRfcGFydGljaXBhdGlvbi9tYWxlX2FkdWx0X3Jlc2lkZW50X3BvcHVsYXRpb24KCiNmZW1hbGUgcGFydGljaXBhdGlvbgpmZW1hbGVfYWR1bHRfcmVzaWRlbnRfcGFydGljaXBhdGlvbi9mZW1hbGVfYWR1bHRfcmVzaWRlbnRfcG9wdWxhdGlvbgoKCmBgYAoKCkxvb2sgYXQgdXB0YWtlIG9mIHNjcmVlbmluZyBieSBhZ2UgYW5kIHNleAoKYGBge3J9CgoKdXB0YWtlX2FnZV9zZXggPC0gcmVhZF94bHN4KCIyMDI0LTAzLTI2X21hc3NfeHJheV91cHRha2UueGxzeCIsIHNoZWV0ID0gInVwdGFrZV9hZ2Vfc2V4IikKCnVwdGFrZV9ncmFwaCA8LSB1cHRha2VfYWdlX3NleCAlPiUKICBtdXRhdGUodXB0YWtlID0gZXhhbWluZWQvcmVzaWRlbnRfcG9wdWxhdGlvbikgJT4lCiAgbXV0YXRlKGV4YW1pbmVkX2wgPSBjb21tYShleGFtaW5lZCksCiAgICAgICAgIHJlc2lkZW50X3BvcHVsYXRpb25fbCA9IGNvbW1hKHJlc2lkZW50X3BvcHVsYXRpb24pLAogICAgICAgICB1cHRha2VfbCA9IHBlcmNlbnQodXB0YWtlLCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUobGFiZWwgPSBnbHVlKCJ7ZXhhbWluZWRfbH0ve3Jlc2lkZW50X3BvcHVsYXRpb25fbH0gKHt1cHRha2VfbH0pIikpICU+JQogIGZpbHRlcihhZ2UgIT0iMDAtMTQiKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Im0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iZiIgfiAiRmVtYWxlIikpICU+JQogIGdncGxvdChhZXMoeD1hZ2UsIHk9dXB0YWtlLCBncm91cD1zZXgsIGZpbGw9c2V4KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXVwdGFrZV9sKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjg1KSx2anVzdD0yKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1wZXJjZW50KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0NEN0FDNSIsICJjYWRldGJsdWUzIiksIG5hbWU9IiIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iIiwgeT0iIikKCgoKCmBgYApDb21iaW5lIGZpZ3VyZSB3aXRoIHRhYmxlIGZvciBzaW5nbGUgZmlndXJlLgoKYGBge3J9Cgp1cHRha2VfdGFibGUgPC0gdXB0YWtlX2FnZV9zZXggJT4lCiAgbXV0YXRlKHJlc2lkZW50X3BvcHVsYXRpb24gPSBjb21tYShyZXNpZGVudF9wb3B1bGF0aW9uKSwKICAgICAgICAgZXhhbWluZWQgPSBjb21tYShleGFtaW5lZCkpICU+JQogIHJlbmFtZShTZXggPSBzZXgsCiAgICAgICAgIEFnZSA9IGFnZSwKICAgICAgICAgYFJlc2lkZW50IHBvcHVsYXRpb25gID0gcmVzaWRlbnRfcG9wdWxhdGlvbiwKICAgICAgICAgRXhhbWluZWQgPSBleGFtaW5lZCkgJT4lCiAgbXV0YXRlKFNleCA9IGNhc2Vfd2hlbihTZXg9PSJtIiB+ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIFNleD09ImYiIH4gIkZlbWFsZSIpKQoKdXB0YWtlX2dyYXBoIC8gZ3JpZEV4dHJhOjp0YWJsZUdyb2IodXB0YWtlX3RhYmxlLCByb3dzID0gTlVMTCkKCmdnc2F2ZShoZXJlKCJmaWd1cmVzL3M1LnRpZmYiKSwgaGVpZ2h0PTEwKQoKYGBgCgoKClVwdGFrZSBieSBkaXZpc2lvbgoKYGBge3J9Cgp1cHRha2VfZGl2aXNpb24gPC0gcmVhZF94bHN4KCIyMDI0LTAzLTI2X21hc3NfeHJheV91cHRha2UueGxzeCIsIHNoZWV0ID0gInVwdGFrZV9kaXZpc2lvbiIpCgpkaXZpc2lvbl9wb3BzICU+JQogIGZpbHRlcih5ZWFyPT0xOTU3KSAlPiUKICBzZWxlY3QoZGl2aXNpb24sIHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXApICU+JQogIGxlZnRfam9pbih1cHRha2VfZGl2aXNpb24pICU+JQogIG11dGF0ZShwY3RfcG9wX2V4YW1pbmVkID0gZXhhbWluZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCkKCgpgYGAKCgoKCiMjIyA1IFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzCgojIyMjIDUuMSBPdmVyYWxsIFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzCgpOb3cgY2FsY3VsYXRlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzIHBlciAxMDAsMDAwIHBvcHVsYXRpb24KCk1lcmdlIHRoZSBub3RpZmljYXRpb24gYW5kIHBvcHVsYXRpb24gZGVub21pbmF0b3IgZGF0YXNldHMgdG9nZXRoZXIuCgpIZXJlIHdlIG5lZWQgdG8gaW5jbHVkZSB0aGUgd2hvbGUgcG9wdWxhdGlvbiAod2l0aCBzaGlwcGluZyBhbmQgaW5zdGl0dXRpb25zKSBhcyB0aGV5IGFyZSBpbmNsdWRlZCBpbiB0aGUgbm90aWZpY2F0aW9ucy4KCmBgYHtyfQoKb3ZlcmFsbF9pbmMgPC0gb3ZlcmFsbF9wb3BzICU+JQogIGxlZnRfam9pbihjYXNlc19ieV95ZWFyKQoKb3ZlcmFsbF9pbmMgPC0gb3ZlcmFsbF9pbmMgJT4lCiAgbXV0YXRlKGluY19wdWxtXzEwMGsgPSBwdWxtb25hcnlfbm90aWZpY2F0aW9ucy90b3RhbF9wb3B1bGF0aW9uKjEwMDAwMCwKICAgICAgICAgaW5jX2VwXzEwMGsgPSBgbm9uLXB1bG1vbmFyeV9ub3RpZmljYXRpb25zYC90b3RhbF9wb3B1bGF0aW9uKjEwMDAwMCwKICAgICAgICAgaW5jXzEwMGsgPSB0b3RhbF9ub3RpZmljYXRpb25zL3RvdGFsX3BvcHVsYXRpb24qMTAwMDAwKQoKb3ZlcmFsbF9pbmMgJT4lCiAgc2VsZWN0KHllYXIsIGluY18xMDBrLCBpbmNfcHVsbV8xMDBrLCBpbmNfZXBfMTAwaykgJT4lCiAgbXV0YXRlX2F0KC52YXJzID0gdmFycyhpbmNfMTAwaywgaW5jX3B1bG1fMTAwaywgaW5jX2VwXzEwMGspLAogICAgICAgICAgICAuZnVucyA9IGZ1bnMocm91bmQpKSAlPiUKICBkYXRhdGFibGUoKQoKYGBgCgpgYGB7cn0KCm92ZXJhbGxfaW5jICU+JQogIHNlbGVjdCh5ZWFyMiwgaW5jX3B1bG1fMTAwaywgaW5jX2VwXzEwMGspICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhpbmNfcHVsbV8xMDBrLCBgaW5jX2VwXzEwMGtgKSkgJT4lCiAgbXV0YXRlKG5hbWUgPSBjYXNlX3doZW4obmFtZSA9PSAiaW5jX3B1bG1fMTAwayIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID09ICJpbmNfZXBfMTAwayIgfiAiRXh0cmEtcHVsbW9uYXJ5IFRCIikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9dmFsdWUsIHg9eWVhcjIsIGdyb3VwID0gbmFtZSwgZmlsbD1uYW1lKSwgYWxwaGE9MC41KSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbmFtZT0iIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBUdWJlcmN1bG9zaXMgY2FzZSBub3RpZmljYXRpb24gcmF0ZSIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMsIGJ5IFRCIGRpc2Vhc2UgY2xhc3NpZmljYXRpb24iLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiQ2FzZSBub3RpZmljYXRpb24gcmF0ZSAocGVyIDEwMCwwMDApIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCgpgYGAKCkNoYW5nZSBpbiBjYXNlIG5vdGlmaWNhdGlvbiByYXRlcyBwcmUtaW50ZXJ2ZW50aW9uCgpgYGB7cn0KI3ByZS1BQ0YKb3ZlcmFsbF9pbmMgJT4lCiAgZmlsdGVyKHllYXIgJWluJSAxOTUwOjE5NTYpICU+JQogIHN1bW1hcmlzZShjaGFuZ2UgPSAoKChsYXN0KGluY19wdWxtXzEwMGspLWZpcnN0KGluY19wdWxtXzEwMGspKS9maXJzdChpbmNfcHVsbV8xMDBrKSkvNykqMTAwKQoKI3Bvc3QtQUNGCm92ZXJhbGxfaW5jICU+JQogIGZpbHRlcih5ZWFyICVpbiUgMTk1ODoxOTYzKSAlPiUKICBzdW1tYXJpc2UoY2hhbmdlID0gKCgobGFzdChpbmNfcHVsbV8xMDBrKS1maXJzdChpbmNfcHVsbV8xMDBrKSkvZmlyc3QoaW5jX3B1bG1fMTAwaykpLzYpKjEwMCkKCmBgYAoKCgoKIyMjIyA1LjIgVEIgY2FzZSBub3RpZmljYXRpb24gcmF0ZXMgYnkgRGl2aXNpb24KCmBgYHtyfQoKZGl2aXNpb25faW5jIDwtIGRpdmlzaW9uX3BvcHMgJT4lCiAgbGVmdF9qb2luKGNhc2VzX2J5X2RpdmlzaW9uKQoKCmRpdmlzaW9uX2luYyA8LSBkaXZpc2lvbl9pbmMgJT4lCiAgbXV0YXRlKGluY18xMDBrID0gY2FzZXMvdG90YWxfcG9wdWxhdGlvbioxMDAwMDApCgpkaXZpc2lvbl9pbmMgJT4lCiAgc2VsZWN0KHllYXIsIGRpdmlzaW9uLCB0Yl90eXBlLCBpbmNfMTAwaykgJT4lCiAgbXV0YXRlX2F0KC52YXJzID0gdmFycyhpbmNfMTAwayksCiAgICAgICAgICAgIC5mdW5zID0gZnVucyhyb3VuZCkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpgYGB7cn0KCmRpdmlzaW9uX2luYyAlPiUKICBtdXRhdGUodGJfdHlwZSA9IGNhc2Vfd2hlbih0Yl90eXBlID09ICJQdWxtb25hcnkiIH4gIlB1bG1vbmFyeSBUQiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGJfdHlwZSA9PSAiTm9uLVB1bG1vbmFyeSIgfiAiRXh0cmEtcHVsbW9uYXJ5IFRCIikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9aW5jXzEwMGssIHg9eWVhcjIsIGdyb3VwID0gdGJfdHlwZSwgZmlsbD10Yl90eXBlKSwgYWxwaGE9MC41KSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2F4aXMoYW5nbGUgPSA5MCkpICsKICBmYWNldF93cmFwKGRpdmlzaW9ufi4pICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBuYW1lPSIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IFR1YmVyY3Vsb3NpcyBjYXNlIG5vdGlmaWNhdGlvbiByYXRlLCBieSBEaXZpc2lvbiIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMsIGJ5IFRCIGRpc2Vhc2UgY2xhc3NpZmljYXRpb24iLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiQ2FzZSBub3RpZmljYXRpb24gcmF0ZSAocGVyIDEwMCwwMDApIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1Nylcbk5vdGU6IGV4dHJhLXB1bG1vbmFyeSBUQiBjYXNlcyBieSBEaXZpc2lvbi9XYXJkIG5vdCByZXBvcnRlZCBpbiAxOTYyLTE5NjMiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCgpgYGAKCiMjIyMgNS4yIFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzIGJ5IFdhcmQKCkhlcmUgd2Ugd2lsbCBmaWx0ZXIgb3V0IHRoZSBpbnN0aXR1dGlvbnMgYW5kIGhhcmJvdXIgZnJvbSB0aGUgZGVub21pbmF0b3JzLCBhcyB3ZSBkb24ndCBoYXZlIHJlbGlhYmxlIHBvcHVsYXRpb24gZGVub21pbmF0b3JzIGZvciB0aGVtLgoKYGBge3J9Cgp3YXJkX2luYyA8LSB3YXJkX3BvcHMgJT4lCiAgbGVmdF9qb2luKGNhc2VzX2J5X3dhcmQpCgoKd2FyZF9pbmMgPC0gd2FyZF9pbmMgJT4lCiAgbXV0YXRlKGluY18xMDBrID0gY2FzZXMvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApCgp3YXJkX2luYyAlPiUKICBzZWxlY3QoeWVhciwgd2FyZCwgdGJfdHlwZSwgaW5jXzEwMGspICU+JQogIG11dGF0ZV9hdCgudmFycyA9IHZhcnMoaW5jXzEwMGspLAogICAgICAgICAgICAuZnVucyA9IGZ1bnMocm91bmQpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKCmBgYHtyfQoKd2FyZF9pbmMgJT4lCiAgbXV0YXRlKHRiX3R5cGUgPSBjYXNlX3doZW4odGJfdHlwZSA9PSAiUHVsbW9uYXJ5IiB+ICJQdWxtb25hcnkgVEIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRiX3R5cGUgPT0gIk5vbi1QdWxtb25hcnkiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PWluY18xMDBrLCB4PXllYXIyLCBncm91cCA9IHRiX3R5cGUsIGZpbGw9dGJfdHlwZSksIGFscGhhPTAuNSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgZmFjZXRfd3JhcCh3YXJkfi4pICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBuYW1lPSIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IFR1YmVyY3Vsb3NpcyBjYXNlIG5vdGlmaWNhdGlvbiByYXRlLCBieSBXYXJkIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJJbmNpZGVuY2UgKHBlciAxMDAsMDAwKSIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpXG5Ob3RlOiBleHRyYS1wdWxtb25hcnkgVEIgY2FzZXMgYnkgRGl2aXNpb24vV2FyZCBub3QgcmVwb3J0ZWQgaW4gMTk2Mi0xOTYzIgogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCgoKYGBgCgpPbiBhIG1hcAoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CgpzdF9hc19zZihsZWZ0X2pvaW4od2FyZF9pbmMsIGdsYXNnb3dfd2FyZHNfMTk1MSkpICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGw9aW5jXzEwMGspKSArCiAgZmFjZXRfd3JhcCh5ZWFyfi4sIG5jb2wgPSA3KSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZT0iQ2FzZSBub3RpZmljYXRpb24gcmF0ZSAocGVyIDEwMCwwMDApIiwKICAgICAgICAgICAgICAgICAgICAgICBvcHRpb24gPSAiQSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMiwgImNtIiksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2NvbG9yYmFyKHRpdGxlLnBvc2l0aW9uID0gInRvcCIpKQoKCgpgYGAKCgojIyMgNi4gVEIgTW9ydGFsaXR5CgojIyMjIDYuMSBPdmVyYWxsIE1vcnRhbGl0eQoKSW1wb3J0IHRoZSBUQiBtb3J0YWxpdHkgZGF0YS4KCkZpcnN0LCBvdmVyYWxsIGRlYXRocy4gTm90ZSB0aGF0IGluIHRoZSBvcmlnaW5hbCByZXBvcnRzLCB3ZSBoYXZlIGEgcHVsbW9uYXJ5IFRCIGRlYXRoIHJhdGUgcGVyIG1pbGxpb24gZm9yIGFsbCB5ZWFycywgYW5kIG51bWJlcnMgb2YgcHVsbW9uYXJ5IFRCIGRlYXRocyBmb3IgZWFjaCB5ZWFyIGFwYXJ0IGZyb20gMTk1MC4KCmBgYHtyfQoKI2dldCB0aGUgb3ZlcmFsbCBtb3J0YWxpdHkgc2hlZXRzCmRlYXRoc19zaGVldHMgPC0gZW5mcmFtZShhbGxfc2hlZXRzKSAlPiUKICBmaWx0ZXIoZ3JlcGwoImRlYXRocyIsIHZhbHVlKSkgJT4lCiAgcHVsbCh2YWx1ZSkKCgpvdmVyYWxsX2RlYXRocyA8LSBtYXBfZGYoZGVhdGhzX3NoZWV0cywgfnJlYWRfeGxzeChwYXRoID0gIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAuKSkKCm92ZXJhbGxfZGVhdGhzICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgoKYGBgCgpQbG90IHRoZSByYXcgbnVtYmVycyBvZiBwdWxtb25hcnkgZGVhdGhzCgpgYGB7cn0KCm92ZXJhbGxfZGVhdGhzICU+JQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXB1bG1vbmFyeV9kZWF0aHMpKSArCiAgZ2VvbV9saW5lKGNvbG91ciA9ICIjREUwRDkyIikgKwogIGdlb21fcG9pbnQoY29sb3VyID0gIiNERTBEOTIiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIGxhYnMoeT0iUHVsbW9uYXJ5IFRCIGRlYXRocyBwZXIgeWVhciIsCiAgICAgICB4ID0gIlllYXIiLAogICAgICAgdGl0bGUgPSAiTnVtYmVycyBvZiBwdWxtb25hcnkgVEIgZGVhdGhzIiwKICAgICAgIHN1YnRpdGxlID0gIkdsYXNnb3csIDE5NTAtMTk2MyIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpXG5Ob3RlOiBubyBkYXRhIGZvciAxOTUwIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKQoKCmBgYAoKTm93IHRoZSBpbmNpZGVuY2Ugb2YgcHVsbW9uYXJ5IFRCIGRlYXRoCgpgYGB7cn0Kb3ZlcmFsbF9kZWF0aHMgJT4lCiAgZ2dwbG90KGFlcyh4PXllYXIsIHk9cHVsbW9uYXJ5X2RlYXRoX3JhdGVfcGVyXzEwMGspKSArCiAgZ2VvbV9saW5lKGNvbG91ciA9ICIjNEQ2Q0ZBIikgKwogIGdlb21fcG9pbnQoY29sb3VyID0gIiM0RDZDRkEiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzKSArCiAgbGFicyh5PSJBbm51YWwgaW5jaWRlbmNlIG9mIGRlYXRoIChwZXIgMTAwLDAwMCkiLAogICAgICAgeCA9ICJZZWFyIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9kZWF0aHMudGlmZiIpLCB3aWR0aD0xMCkKCmBgYAoKCiMjIyA2LiBUYWJsZSAxCgpNYWtlIFRhYmxlIDEgaGVyZSwgYW5kIHNhdmUgZm9yIHB1YmxpY2F0aW9uLgoKYGBge3J9CgpvdmVyYWxsX3BvcHMgJT4lIAogIHNlbGVjdCh5ZWFyLCB0b3RhbF9wb3B1bGF0aW9uKSAlPiUKICBsZWZ0X2pvaW4ob3ZlcmFsbF9pbmMgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIAogICAgICAgICAgICAgICAgICAgICBwdWxtb25hcnlfbm90aWZpY2F0aW9ucywgaW5jX3B1bG1fMTAwaywKICAgICAgICAgICAgICAgICAgICAgYG5vbi1wdWxtb25hcnlfbm90aWZpY2F0aW9uc2AsIGluY19lcF8xMDBrLAogICAgICAgICAgICAgICAgICAgICB0b3RhbF9ub3RpZmljYXRpb25zLCBpbmNfMTAwaykpICU+JQogIGxlZnRfam9pbihvdmVyYWxsX2RlYXRocyAlPiUKICAgICAgICAgICAgICBzZWxlY3QoeWVhciwKICAgICAgICAgICAgICAgICAgICAgcHVsbW9uYXJ5X2RlYXRocywgcHVsbW9uYXJ5X2RlYXRoX3JhdGVfcGVyXzEwMGspKSAlPiUKICBtdXRhdGUocGVyY2VudF9wdWxtb25hcnkgPSBwZXJjZW50KHB1bG1vbmFyeV9ub3RpZmljYXRpb25zLyh0b3RhbF9ub3RpZmljYXRpb25zICksIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfnJvdW5kKC4sIGRpZ2l0cz0xKSkpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkKCmBgYAoKQ29tcGFyaXNvbiBmbyBhZ2Utc2V4IGRpc3RyaWJ1dGlvbiBvZiBjYXNlcyBpbiAxOTUwLTE5NTYgdnMuIDE5NTcKCmBgYHtyfQoKbGFiZWxfYWJzMiA8LSBmdW5jdGlvbih4KSB7CiAgcGVyY2VudChhYnMoeCkpCn0KCgoKY2FzZXNfYnlfYWdlX3NleCAlPiUgCiAgdW5ncm91cCgpICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oeWVhciAlaW4lIGMoMTk1MDoxOTU2KSB+ICJhLiBwcmUtYWNmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyICVpbiUgYygxOTU3KSB+ICJiLiBhY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTg6MTk2MykgfiAiYy4gcG9zdC1hY2YiKSkgJT4lCiAgZ3JvdXBfYnkoYWNmX3BlcmlvZCwgYWdlLCBzZXgpICU+JQogIHN1bW1hcmlzZShjYXNlcyA9IHN1bShjYXNlcykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieShhY2ZfcGVyaW9kKSAlPiUKICBtdXRhdGUocGVyaW9kX3RvdGFsID0gc3VtKGNhc2VzKSkgJT4lCiAgbXV0YXRlKHBjdCA9IGNhc2VzL3BlcmlvZF90b3RhbCkgJT4lCiAgbXV0YXRlKHBjdDIgPSBjYXNlX3doZW4oc2V4PT0iRiIgfiAtcGN0LAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBwY3QpKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Ik0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iRiIgfiAiRmVtYWxlIikpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgICAgICAgICAgICAgICAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIlByZS1BQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJiLiBhY2YiIH4gIkFDRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZD09ImMuIHBvc3QtYWNmIiB+ICJQb3N0LUFDRiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD0wKSwgbGluZXR5cGU9MikgKwogIGdlb21fcG9pbnQoYWVzKHg9cGN0Mix5PWFnZSwgY29sb3VyPWZjdF9yZWxldmVsKGFjZl9wZXJpb2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHJlLUFDRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQb3N0LUFDRiIpKSwgc3RhdD0iaWRlbnRpdHkiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1sYWJlbF9hYnMyLCBsaW1pdHMgPSBjKC0wLjIsIDAuMikpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSkgKwogIHRoZW1lX2dyZXkoYmFzZV9mYW1pbHkgPSAiQXB0b3MiKSArCiAgbGFicyh4PSAiPC0gRmVtYWxlICAgICAgICAgICAgICAgIFBlcmNlbnQgb2YgY2FzZXMgICAgICAgICAgICAgIE1hbGUgLT4iLAogICAgICAgeT0iIikgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmdnc2F2ZShoZXJlKCJmaWd1cmVzL3M2LnRpZmYiKSkKCmBgYAoKCgpQcmVwYXJlIHRoZSBkYXRhc2V0cyBmb3IgbW9kZWxsaW5nCgpgYGB7cn0KCm1kYXRhIDwtIHdhcmRfaW5jICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oeWVhciAlaW4lIGMoMTk1MDoxOTU2KSB+ICJhLiBwcmUtYWNmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyICVpbiUgYygxOTU3KSB+ICJiLiBhY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTg6MTk2MykgfiAiYy4gcG9zdC1hY2YiKSkgJT4lCiAgZ3JvdXBfYnkod2FyZCkgJT4lCiAgbXV0YXRlKHlfbnVtID0gcm93X251bWJlcigpKSAlPiUKICB1bmdyb3VwKCkKCgptZGF0YV9leHRyYXB1bG1vbmFyeSA8LSB3YXJkX2luYyAlPiUKICBmaWx0ZXIodGJfdHlwZT09Ik5vbi1QdWxtb25hcnkiKSAlPiUKICBtdXRhdGUoYWNmX3BlcmlvZCA9IGNhc2Vfd2hlbih5ZWFyICVpbiUgYygxOTUwOjE5NTYpIH4gImEuIHByZS1hY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTcpIH4gImIuIGFjZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciAlaW4lIGMoMTk1ODoxOTYzKSB+ICJjLiBwb3N0LWFjZiIpKSAlPiUKICBncm91cF9ieSh3YXJkKSAlPiUKICBtdXRhdGUoeV9udW0gPSByb3dfbnVtYmVyKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgZmlsdGVyKHllYXI8PTE5NjEpICNubyBkYXRhIGZvciAxOTYyIGFuZCAxOTYzCgoKI3NjYWZmb2xkIGZvciBvdmVyYWxsIHByZWRpY3Rpb25zCm92ZXJhbGxfc2NhZmZvbGQgPC0gbWRhdGEgJT4lCiAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhY2ZfcGVyaW9kLCBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCB3YXJkLCBjYXNlcykgJT4lCiAgICBncm91cF9ieSh5ZWFyLCB5ZWFyMiwgeV9udW0sIGFjZl9wZXJpb2QpICU+JQogICAgc3VtbWFyaXNlKHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAgPSBzdW0ocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCksCiAgICAgICAgICAgICAgY2FzZXMgPSBzdW0oY2FzZXMpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShpbmNfMTAwayA9IGNhc2VzL3BvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAqMTAwMDAwKSAlPiUKICAgIGxlZnRfam9pbihtZGF0YV9leHRyYXB1bG1vbmFyeSAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lCiAgICAgICAgICAgICAgICBzdW1tYXJpc2UoY2FzZXNfZXh0cmFwdWxtb25hcnkgPSBzdW0oY2FzZXMpKSkgJT4lCiAgICBtdXRhdGUoaW5jXzEwMGtfZXh0cmFwdWxtb25hcnkgPSBjYXNlc19leHRyYXB1bG1vbmFyeS9wb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKjEwMDAwMCkKCmBgYAoKCiMjIyA3LiBQdWxtb25hcnkgVEIgbW9kZWwKCiMjIyMgNy4xIEZpdCB0aGUgbW9kZWwKCgpMb29rIGF0IHRoZSBtZWFuIGFuZCB2YXJpYW5jZSBvZiBjb3VudHMgKGNvdW50cyBvZiBwdWxtb25hcnkgbm90aWZpY2F0aW9ucyBhcmUgd2hhdCB3ZSBhcmUgcHJlZGljdGluZykKCmBgYHtyfQoKI01lYW4gb2YgY291bnRzIHBlciB5ZWFyCm1lYW4obWRhdGEkY2FzZXMpCiN2YXJpYW5jZSBvZiBjb3VudHMgcGVyIHllYXIKdmFyKG1kYXRhJGNhc2VzKQoKYGBgCgoKUXVpdGUgYSBiaXQgb2Ygb3Zlci1kaXNwZXJzaW9uIGhlcmUsIHNvIG5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiBtaWdodCBiZSBhIGJldHRlciBjaG9pY2Ugb2YgZGlzdHJpYnV0aW9uYWwgZmFtaWx5IHRoYW4gUG9pc3Nvbi4KCkZpdCB0aGUgbW9kZWwgd2l0aCB0aGUgZGF0YQoKYGBge3J9CgptX3B1bG1vbmFyeSA8LSBicm0oCiAgY2FzZXMgfiAwICsgSW50ZXJjZXB0ICsgeV9udW0qYWNmX3BlcmlvZCArICgxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpICsgb2Zmc2V0KGxvZyhwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBtZGF0YSwKICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gbmVnYmlub21pYWwoKSwKICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQsCiAgICAgICAgICAgICAgICAgIHRocmVhZHMgPSB0aHJlYWRpbmcoMiwgZ3JhaW5zaXplID0gMTAwLCBzdGF0aWMgPSBUUlVFKSwgI2ZvciBleGFjdCByZXByb2R1Y2liaWxpdHkKICAgICAgICAgICAgICAgICAgYmFja2VuZCA9ICJjbWRzdGFuciIsCiAgICAgICAgICAgICAgICAgIGNoYWlucyA9IDQsIGNvcmVzID0gNCwKICAgICAgICAgICAgICAgICAgcHJpb3IgPSBwcmlvcihub3JtYWwoMCwxKSwgY2xhc3M9YiwgY29lZiA9ICJJbnRlcmNlcHQiKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IoZ2FtbWEoMC4wMSwgMC4wMSksIGNsYXNzID0gc2hhcGUpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihub3JtYWwoMCwgMSksIGNsYXNzID0gYikgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGV4cG9uZW50aWFsKDEpLCBjbGFzcz1zZCkgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGxraig0KSwgY2xhc3M9Y29yKSwKICBjb250cm9sID0gbGlzdChhZGFwdF9kZWx0YSA9IDAuOSkpCiAgCiNjaGVjayBtb2RlbCBkaWFnbm9zdGljcwpzdW1tYXJ5KG1fcHVsbW9uYXJ5KQpwbG90KG1fcHVsbW9uYXJ5KQoKcHBfY2hlY2sobV9wdWxtb25hcnksIHR5cGU9J2VjZGZfb3ZlcmxheScpCnByaW9yX3N1bW1hcnkobV9wdWxtb25hcnkpCgpgYGAKCk5pY2VyIHZlcnNpb24gb2YgdHJhY2UgcGxvdHMgZm9yIHN1cHBsZW1lbnRhbCBtYXRlcmlhbAoKYGBge3IsIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0xNn0KCmFzX2RyYXdzX2RmKG1fcHVsbW9uYXJ5KSAlPiUgCiAgYmF5ZXNwbG90OjptY21jX3Jhbmtfb3ZlcmxheShwYXJzID0gdmFycyhiX0ludGVyY2VwdDpzaGFwZSksCiAgICAgICAgICAgICBmYWNldF9hcmdzID0gbGlzdChuY29sID0gNCkpICsKICBzY2FsZV9jb2xvdXJfc2NpY29fZChwYWxldHRlID0gIm1hbmFndWEiLCBuYW1lID0gIkNoYWluIikgKwogIHRoZW1lX2dnZGlzdCgpKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9zOC50aWZmIiksIHdpZHRoPTE2LCBoZWlnaHQ9MTYpCmBgYAoKTmljZXIgdmVyc2lvbiBvZiB0YWJsZSBvZiBwYXJhbWV0ZXJzIGZvciBzdXBwbGVtZW50CgpgYGB7cn0KCnN1bW1hcmlzZV9kcmF3cyhtX3B1bG1vbmFyeSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKG1lYW46ZXNzX3RhaWwpLCBjb21tYSwgYWNjdXJhY3k9MC4wMSkpICU+JQogIHdyaXRlX2NzdihoZXJlKCJmaWd1cmVzL3M5X3RhYmxlLmNzdiIpKQoKYGBgCgoKCiMjIyMgNy4yIFN1bW1hcmlzZSBjaGFuZ2UgaW4gQ05ScwoKU3VtbWFyaXNlIHRoZSBwb3N0ZXJpb3IgaW4gZ3JhcGhpY2FsIGZvcm0KCmBgYHtyfQoKZjFiIDwtIHBsb3RfY291bnRlcmZhY3R1YWwobW9kZWxfZGF0YSA9IG92ZXJhbGxfc2NhZmZvbGQsIG1vZGVsID0gbV9wdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgb3V0Y29tZSA9IGluY18xMDBrLCBncm91cGluZ192YXI9TlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IE5BKQogIApmMWIKYGBgCgpNYWtlIHRoaXMgaW50byBhIGZpZ3VyZSBjb21iaW5lZCB3aXRoIHRoZSBtYXAgb2YgZW1waXJpY2FsIGRhdGEKCmBgYHtyfQoKZjFhIDwtIHN0X2FzX3NmKGxlZnRfam9pbih3YXJkX2luYywgZ2xhc2dvd193YXJkc18xOTUxKSkgJT4lCiAgZmlsdGVyKHRiX3R5cGU9PSJQdWxtb25hcnkiKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoZmlsbD1pbmNfMTAwayksIGNvbG91cj0iZ3JleTk4IiwgbHdkPTAuMDEpICsKICBmYWNldF93cmFwKHllYXJ+LiwgbmNvbCA9IDcpICsKICBzY2FsZV9maWxsX3NjaWNvKG5hbWU9IkNOUiAocGVyIDEwMCwwMDApIiwKICAgICAgICAgICAgICAgICAgICAgICBwYWxldHRlID0gImFjdG9uIiwgZGlyZWN0aW9uID0gLTEpICsKICB0aGVtZV9ncmV5KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgICNsZWdlbmQua2V5LndpZHRoID0gdW5pdCgxLCAiY20iKSwKICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLjUsCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpCgooZjFhIC8gZjFiKSArIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gIkEiKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvZjEudGlmZiIpLCB3aWR0aD03LCBoZWlnaHQ9OCkKCmBgYAoKU3VtbWFyeSBvZiBjaGFuZ2UgaW4gbm90aWZpY2F0aW9ucyBudW1lcmljYWxseQoKYGBge3J9CgpvdmVyYWxsX2NoYW5nZSA8LSBzdW1tYXJpc2VfY2hhbmdlKG1vZGVsX2RhdGE9b3ZlcmFsbF9zY2FmZm9sZCwgbW9kZWw9bV9wdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3I9cG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgZ3JvdXBpbmdfdmFyPU5VTEwsIHJlX2Zvcm11bGEgPSBOQSkKCiN3YW50IHRvIGtlZXAgdGhlIHN1bW1hcnkgZXN0aW1hdGVzIGhlcmUKdG9rZWVwIDwtIGMoInBlYWtfc3VtbWFyeSIsICJsZXZlbF9zdW1tYXJ5IiwgInNsb3BlX3N1bW1hcnkiKQoKI3N1bW1hcnkgbWVhc3VyZXMgaW4gYSB0YWJsZQpvdmVyYWxsX2NoYW5nZSAlPiUKICBrZWVwKChuYW1lcyguKSAlaW4lIHRva2VlcCkpICU+JQogIGJpbmRfcm93cygpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhlc3RpbWF0ZToudXBwZXIpLCBudW1iZXIsIGFjY3VyYWN5PTAuMDEpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBkYXRhdGFibGUoKQoKICAKYGBgCgoKIyMjIyA3LjMgQ29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwKCk51bWJlcnMgb2YgcHVsbW9uYXJ5IFRCIGNhc2VzIGF2ZXJ0ZWQgY29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwgcGVyIHllYXIuCgpgYGB7cn0KCm92ZXJhbGxfcHVsbW9uYXJ5X2NvdW50ZXJmIDwtIGNhbGN1bGF0ZV9jb3VudGVyZmFjdHVhbChtb2RlbF9kYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCwgbW9kZWw9bV9wdWxtb25hcnksIHBvcHVsYXRpb25fZGVub21pbmF0b3IgPSBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKQoKb3ZlcmFsbF9wdWxtb25hcnlfY291bnRlcmYkY291bnRlcl9wb3N0ICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIsIGRpZmZfaW5jMTAwazpkaWZmX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocnJfaW5jMTAwazpycl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMDEpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpUb3RhbCBwdWxtb25hcnkgVEIgY2FzZXMgYXZlcnRlZCBiZXR3ZWVuIDE5NTggYW5kIDE5NjMKCmBgYHtyfQoKb3ZlcmFsbF9wdWxtb25hcnlfY291bnRlcmYkY291bnRlcl9wb3N0X292ZXJhbGwgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKIyMjIyA3LjQgQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrLCBSUi5sZXZlbCwgYW5kIFJSLnNsb3BlCgpXaGF0IGFyZSB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gcGVhaywgbGV2ZWwsIGFuZCBzbG9wZT8KCmBgYHtyfQoKI1JSLnBlYWsgaGlzdG9ncmFtCmEgPC0gb3ZlcmFsbF9jaGFuZ2UkcGVha19kcmF3cyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9ZXN0aW1hdGUpLCBmaWxsPSJkYXJrYmx1ZSIsIGNvbG91cj0iZGFya2JsdWUiLCBhbHBoYT0wLjMpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpICsKICBsYWJzKHg9IlJSLnBlYWsiLAogICAgICAgeT0iIikKCiNSUi4gbGV2ZWwgaGlzdG9ncmFtCmIgPC0gb3ZlcmFsbF9jaGFuZ2UkbGV2ZWxfZHJhd3MgICU+JQogIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeD1lc3RpbWF0ZSksIGZpbGw9ImRhcmtibHVlIiwgY29sb3VyPSJkYXJrYmx1ZSIsIGFscGhhPTAuMykrCiAgc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSJsaWdodGJsdWUxIixsb3c9ImRhcmtibHVlIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iUlIubGV2ZWwiLAogICAgICAgeT0iIikKCiNSUi5zbG9wZSBoaXN0b2dyYW0KYyA8LSBvdmVyYWxsX2NoYW5nZSRzbG9wZV9kcmF3cyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9ZXN0aW1hdGUpLCBmaWxsPSJkYXJrYmx1ZSIsIGNvbG91cj0iZGFya2JsdWUiLCBhbHBoYT0wLjMpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsgIAogICNzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA2KSkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iUlIuc2xvcGUiLAogICAgICAgeT0iIikKCgojQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrIGFuZCBSUi5sZXZlbApjb3JfcnJfcGVha19ycl9sZXZlbCA8LSByb3VuZChjb3IocGx1Y2sob3ZlcmFsbF9jaGFuZ2UkcGVha19kcmF3cyRlc3RpbWF0ZSksIHBsdWNrKG92ZXJhbGxfY2hhbmdlJGxldmVsX2RyYXdzJGVzdGltYXRlKSksIGRpZ2l0cyA9IDIpCgojQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrIGFuZCBSUi5zbG9wZQpjb3JfcnJfcGVha19ycl9zbG9wZSA8LSByb3VuZChjb3IocGx1Y2sob3ZlcmFsbF9jaGFuZ2UkcGVha19kcmF3cyRlc3RpbWF0ZSksIHBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSksIGRpZ2l0cyA9IDIpCgojQ29ycmVsYXRpb24gYmV0d2VlbiBSUi5sZXZlbCBhbmQgUlIuc2xvcGUKY29yX3JyX2xldmVsX3JyX3Nsb3BlIDwtIHJvdW5kKGNvcihwbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSksIHBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSksIGRpZ2l0cyA9IDIpCgoKI3Bsb3Qgb2YgY29ycmVsYXRpb24gYmV0d2VlbiBSUi5wZWFrIGFuZCBSUi5sZXZlbApkIDwtIGJpbmRfY29scyhSUi5wZWFrPXBsdWNrKG92ZXJhbGxfY2hhbmdlJHBlYWtfZHJhd3MkZXN0aW1hdGUpLCAKICAgICAgICAgIFJSLmxldmVsID1wbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSkpICU+JQogIGdncGxvdChhZXMoeT1SUi5wZWFrLCB4ID0gUlIubGV2ZWwpKSArCiAgZ2VvbV9oZXgoKSArCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UsIGNvbG91cj0iZmlyZWJyaWNrIiwgbWV0aG9kID0gImxtIikgKwogIGdlb21fdGV4dChhZXMoeT0yLjIsIHg9MC41OCwgbGFiZWw9Y29yX3JyX3BlYWtfcnJfbGV2ZWwpLCBjb2xvdXI9ImZpcmVicmljayIpICArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSJsaWdodGJsdWUxIixsb3c9ImRhcmtibHVlIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKCiNwbG90IG9mIGNvcnJlbGF0aW9uIGJldHdlZW4gUlIucGVhayBhbmQgUlIuc2xvcGUKZSA8LSBiaW5kX2NvbHMoUlIucGVhaz1wbHVjayhvdmVyYWxsX2NoYW5nZSRwZWFrX2RyYXdzJGVzdGltYXRlKSwgCiAgICAgICAgICBSUi5zbG9wZSA9cGx1Y2sob3ZlcmFsbF9jaGFuZ2Ukc2xvcGVfZHJhd3MkZXN0aW1hdGUpKSAlPiUKICBnZ3Bsb3QoYWVzKHk9UlIucGVhaywgeCA9IFJSLnNsb3BlKSkgKwogIGdlb21faGV4KCkgKwogIGdlb21fc21vb3RoKHNlPUZBTFNFLCBjb2xvdXI9ImZpcmVicmljayIsIG1ldGhvZCA9ICJsbSIpICsKICBnZW9tX3RleHQoYWVzKHk9Mi4xLCB4PTAuNjUsIGxhYmVsPWNvcl9ycl9wZWFrX3JyX3Nsb3BlKSwgY29sb3VyPSJmaXJlYnJpY2siKSAgKwogICNzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA2KSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgojcGxvdCBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIFJSLmxldmVsIGFuZCBSUi5zbG9wZQpmIDwtIGJpbmRfY29scyhSUi5sZXZlbD1wbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSksIAogICAgICAgICAgUlIuc2xvcGUgPXBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSkgJT4lCiAgZ2dwbG90KGFlcyh5PVJSLmxldmVsLCB4ID0gUlIuc2xvcGUpKSArCiAgZ2VvbV9oZXgoKSArCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UsIGNvbG91cj0iZmlyZWJyaWNrIiwgbWV0aG9kID0gImxtIikgKwogIGdlb21fdGV4dChhZXMoeT0wLjc1LCB4PTAuNjUsIGxhYmVsPWNvcl9ycl9sZXZlbF9ycl9zbG9wZSksIGNvbG91cj0iZmlyZWJyaWNrIikgICsgIAogICNzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA2KSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgoKKHBsb3Rfc3BhY2VyKCkgKyBwbG90X3NwYWNlcigpICsgYykgLwogIChwbG90X3NwYWNlcigpICsgYiArIGYpIC8KICAoYSArIGQgKyBlKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczEwLnRpZmYiKSwgd2lkdGg9OCwgaGVpZ2h0PTgpCgoKCmBgYAoKCiMjIyMgNy41IFdhcmQgbGV2ZWwgcHVsbW9uYXJ5IFRCIGVzdGltYXRlcwoKUGxvdCB0aGUgY291bnRlcmZhY3R1YWwgYXQgd2FyZCBsZXZlbAoKYGBge3J9CgpwbG90X2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBtZGF0YSwgbW9kZWw9bV9wdWxtb25hcnksIG91dGNvbWUgPSBpbmNfMTAwaywgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIAogICAgICAgICAgICAgICAgICAgIGdyb3VwaW5nX3ZhciA9IHdhcmQsIHdhcmQsIHJlX2Zvcm11bGE9IH4oMSArIHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsNTAwKSkKICAKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczcudGlmZiIpLCB3aWR0aD0xNiwgaGVpZ2h0PTEyKQoKYGBgCgpTdW1tYXJ5IG9mIGNoYW5nZSBpbiBub3RpZmljYXRpb25zIGF0IHdhcmQgbGV2ZWwKCmBgYHtyfQoKd2FyZF9jaGFuZ2UgPC0gc3VtbWFyaXNlX2NoYW5nZShtb2RlbF9kYXRhPW1kYXRhLCBtb2RlbD1tX3B1bG1vbmFyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvcj1wb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCBncm91cGluZ192YXI9d2FyZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IH4oMSArIHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSkKCiN3YW50IHRvIGtlZXAgdGhlIHN1bW1hcnkgZXN0aW1hdGVzIGhlcmUKdG9rZWVwIDwtIGMoInBlYWtfc3VtbWFyeSIsICJsZXZlbF9zdW1tYXJ5IiwgInNsb3BlX3N1bW1hcnkiKQoKI3N1bW1hcnkgbWVhc3VyZXMgaW4gYSB0YWJsZQp3YXJkX2NoYW5nZSAlPiUKICBrZWVwKChuYW1lcyguKSAlaW4lIHRva2VlcCkpICU+JQogIGJpbmRfcm93cygpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhlc3RpbWF0ZToudXBwZXIpLCBudW1iZXIsIGFjY3VyYWN5PTAuMDEpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBkYXRhdGFibGUoKQoKCiNwbG90IHRoZXNlIGluIGEgZmlndXJlCndhcmRfZWZmZWN0cyA8LSB3YXJkX2NoYW5nZSAlPiUKICBrZWVwKChuYW1lcyguKSAlaW4lIHRva2VlcCkpICU+JQogIGJpbmRfcm93cygpICU+JQogIGJpbmRfcm93cyhvdmVyYWxsX2NoYW5nZSRwZWFrX3N1bW1hcnkpICU+JQogIGJpbmRfcm93cyhvdmVyYWxsX2NoYW5nZSRsZXZlbF9zdW1tYXJ5KSAlPiUKICBiaW5kX3Jvd3Mob3ZlcmFsbF9jaGFuZ2Ukc2xvcGVfc3VtbWFyeSkgJT4lCiAgbXV0YXRlX2F0KC52YXJzID0gdmFycyhlc3RpbWF0ZToudXBwZXIpLCAKICAgICAgICAgICAgLmZ1bnMgPSBmdW5zKGFzLm51bWVyaWMpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBtdXRhdGUoZXN0aW1hdGUgPSBhcy5kb3VibGUoZXN0aW1hdGUpKSAlPiUKICBmdWxsX2pvaW4oZ2xhc2dvd193YXJkc18xOTUxKSAlPiUgCiAgbXV0YXRlKHdhcmQyID0gcGFzdGUwKHdhcmRfbnVtYmVyLCAiLiAiLCB3YXJkKSkgJT4lCiAgbXV0YXRlKHdhcmQyID0gY2FzZV93aGVuKGlzLm5hKHdhcmQpIH4gIk92ZXJhbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB3YXJkMikpICU+JQogIHN0X2FzX3NmKCkgCgojZnVuY3Rpb24gZm9yIHBsb3R0aW5nIGNob3JvcGxldGggbWFwcwpwbG90X3dhcmRfZWZmZWN0IDwtIGZ1bmN0aW9uKGRhdGEsIG1lYXN1cmUpewogIHt7ZGF0YX19ICU+JQogIGZpbHRlcihtZWFzdXJlID09IHt7bWVhc3VyZX19KSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoZmlsbD1lc3RpbWF0ZSkpICsKICBnZW9tX3NmX2xhYmVsKGFlcyhsYWJlbCA9IHdhcmRfbnVtYmVyKSwgc2l6ZT0zLCBmaWxsPU5BLCBsYWJlbC5zaXplID0gTkEsIGNvbG91cj0iYmxhY2siKSArCiAgc2NhbGVfZmlsbF9zY2ljbyh0cmFucz0ibG9nIiwgcGFsZXR0ZSA9ICJyb21hIiwgbWlkcG9pbnQgPSAwLCBsaW1pdHM9YygwLjUsMi4yNSksCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygwLjUsIDAuNzUsIDEsIDEuNSwgMiwgMi41KSwgbGFiZWxzID0gYygwLjUsIDAuNzUsIDEsIDEuNSwgMiwgMi41KSwKICAgICAgICAgICAgICAgICAgIG5hbWU9IlJlbGF0aXZlIHJhdGUiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKwogICAgbGFicyh4PSIiLCB5PSIiKQp9CgojZnVuY3Rpb24gZm9yIHBsb3R0aW5nIGNhdGFwaWxsZXIgcGxvdHMKcGxvdF93YXJkX2NhdCA8LSBmdW5jdGlvbihkYXRhLCBtZWFzdXJlLCBzY2FsZSl7CgogICAgZ2dwbG90KCkgKwogICAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD0xKSwgbGluZXR5cGU9MikgKwogICAgZ2VvbV9wb2ludHJhbmdlKGRhdGEgPSB7e2RhdGF9fSAlPiUgICAgIAogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKG1lYXN1cmU9PXt7bWVhc3VyZX19KSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighaXMubmEod2FyZCkpLAogICAgICAgICAgICAgICAgICAgIGFlcyh5PWVzdGltYXRlLCB5bWluPS5sb3dlciwgeW1heD0udXBwZXIsIAogICAgICAgICAgICAgICAgICAgICAgeD1mY3RfcmVvcmRlcih3YXJkMiwgZXN0aW1hdGUpLCBjb2xvdXI9ZXN0aW1hdGUpKSArCiAgICBnZW9tX3BvaW50cmFuZ2UoZGF0YSA9IHt7ZGF0YX19ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihtZWFzdXJlPT17e21lYXN1cmV9fSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoaXMubmEod2FyZCkpLAogICAgICAgICAgICAgICAgICAgIGFlcyh5PWVzdGltYXRlLCB5bWluPS5sb3dlciwgeW1heD0udXBwZXIsIAogICAgICAgICAgICAgICAgICAgICAgeD13YXJkMiksIGNvbG91cj0iYmxhY2siKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgc2NhbGVfY29sb3VyX3NjaWNvKHRyYW5zPSJsb2ciLCBwYWxldHRlID0gInJvbWEiLCBtaWRwb2ludCA9IDAsIGxpbWl0cz1jKDAuNSwyLjI1KSwgCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygwLjUsIDAuNzUsIDEsIDEuNSwgMiwgMi41KSwgbGFiZWxzID0gYygwLjUsIDAuNzUsIDEsIDEuNSwgMiwgMi41KSwKICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSJSZWxhdGl2ZSByYXRlIikgKwogICAgc2NhbGVfeV9jb250aW51b3VzKCkgKwogICAgdGhlbWVfZ2dkaXN0KCkgICsKICAgIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpICsKICAgIGxhYnMoeCA9ICIiLAogICAgICAgICB5ID0gIlJlbGF0aXZlIHJhdGUgKDk1JSBVSSkiKSsKICAgIGd1aWRlcyh4ID0gImF4aXNfdHJ1bmNhdGVkIiwgeSA9ICJheGlzX3RydW5jYXRlZCIpCn0KCgoKd2FyZF9wZWFrX2kgPC0gcGxvdF93YXJkX2VmZmVjdChkYXRhID0gd2FyZF9lZmZlY3RzLCBtZWFzdXJlID0gIlJSLnBlYWsiKSArIGdndGl0bGUoIlBlYWsgZWZmZWN0IikKd2FyZF9sZXZlbF9pIDwtIHBsb3Rfd2FyZF9lZmZlY3QoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5sZXZlbCIpICsgZ2d0aXRsZSgiTGV2ZWwgZWZmZWN0IikKd2FyZF9zbG9wZV9pIDwtIHBsb3Rfd2FyZF9lZmZlY3QoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5zbG9wZSIpICsgZ2d0aXRsZSgiU2xvcGUgZWZmZWN0IikKCndhcmRfcGVha19paSA8LSBwbG90X3dhcmRfY2F0KGRhdGEgPSB3YXJkX2VmZmVjdHMsIG1lYXN1cmUgPSAiUlIucGVhayIpICsgZ2d0aXRsZSgiUGVhayBlZmZlY3QiKQp3YXJkX2xldmVsX2lpIDwtIHBsb3Rfd2FyZF9jYXQoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5sZXZlbCIpICsgZ2d0aXRsZSgiTGV2ZWwgZWZmZWN0IikKd2FyZF9zbG9wZV9paSA8LSBwbG90X3dhcmRfY2F0KGRhdGEgPSB3YXJkX2VmZmVjdHMsIG1lYXN1cmUgPSAiUlIuc2xvcGUiKSArIGdndGl0bGUoIlNsb3BlIGVmZmVjdCIpCgpzNCA8LSAod2FyZF9wZWFrX2kgKyB3YXJkX2xldmVsX2kgKyB3YXJkX3Nsb3BlX2kpIC8KICAod2FyZF9wZWFrX2lpICsgd2FyZF9sZXZlbF9paSArIHdhcmRfc2xvcGVfaWkpCgpzNFtbMV1dIDwtIHM0W1sxXV0gKyBwbG90X2xheW91dCh0YWdfbGV2ZWwgPSAnbmV3JykKczRbWzJdXSA8LSBzNFtbMl1dICsgcGxvdF9sYXlvdXQodGFnX2xldmVsID0gJ25ldycpCnM0ICsgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSBjKCdBJywgJzEnKSkgKyBwbG90X2xheW91dChndWlkZXMgPSAnY29sbGVjdCcpICYKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMywgImNtIikpCgoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvZjIudGlmZiIpLCB3aWR0aCA9IDE2LCBoZWlnaHQ9MTIpCgpgYGAKCkNhbGN1bGF0ZSB0aGUgY291bnRlcmZhY3R1YWwgcGVyIHdhcmQKCmBgYHtyfQoKd2FyZF9wdWxtb25hcnlfY291bnRlcmYgPC0gY2FsY3VsYXRlX2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBtZGF0YSwgbW9kZWw9bV9wdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cGluZ192YXIgPSB3YXJkLCByZV9mb3JtdWxhPX4oMSArIHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSkKCndhcmRfcHVsbW9uYXJ5X2NvdW50ZXJmJGNvdW50ZXJfcG9zdCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyLCBkaWZmX2luYzEwMGs6ZGlmZl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHJyX2luYzEwMGs6cnJfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjAxKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKT3ZlcmFsbCBjb3VudGVyZmFjdHVhbCBwZXIgd2FyZAoKYGBge3J9Cgp3YXJkX3B1bG1vbmFyeV9jb3VudGVyZiRjb3VudGVyX3Bvc3Rfb3ZlcmFsbCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIGRhdGF0YWJsZSgpCgpgYGAKCgoKIyMjIDguIEV4dHJhLXB1bG1vbmFyeSBUQiBub3RpZmljYXRpb25zCgojIyMjIDguMSBGaXQgdGhlIG1vZGVsCgpgYGB7cn0KCm1fZXh0cmFwdWxtb25hcnkgPC0gYnJtKAogIGNhc2VzIH4gMCArIEludGVyY2VwdCArIHlfbnVtKmFjZl9wZXJpb2QgKyAoMSArIHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSArIG9mZnNldChsb2cocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCkpLAogICAgICAgICAgICAgICAgICBkYXRhID0gbWRhdGFfZXh0cmFwdWxtb25hcnksCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9IG5lZ2Jpbm9taWFsKCksCiAgICAgICAgICAgICAgICAgIHNlZWQgPSAxMjM0LAogICAgICAgICAgICAgICAgICB0aHJlYWRzID0gdGhyZWFkaW5nKDIsIGdyYWluc2l6ZSA9IDEwMCwgc3RhdGljID0gVFJVRSksICNmb3IgZXhhY3QgcmVwcm9kdWNpYmlsaXR5CiAgICAgICAgICAgICAgICAgIGJhY2tlbmQgPSAiY21kc3RhbnIiLAogICAgICAgICAgICAgICAgICBjaGFpbnMgPSA0LCBjb3JlcyA9IDQsCiAgICAgICAgICAgICAgICAgIHByaW9yID0gcHJpb3Iobm9ybWFsKDAsMSksIGNsYXNzPWIsIGNvZWYgPSAiSW50ZXJjZXB0IikgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGdhbW1hKDAuMDEsIDAuMDEpLCBjbGFzcyA9IHNoYXBlKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3Iobm9ybWFsKDAsIDEpLCBjbGFzcyA9IGIpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihleHBvbmVudGlhbCgxKSwgY2xhc3M9c2QpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihsa2ooNCksIGNsYXNzPWNvcikpCgpzdW1tYXJ5KG1fZXh0cmFwdWxtb25hcnkpCnBsb3QobV9leHRyYXB1bG1vbmFyeSkKcHBfY2hlY2sobV9leHRyYXB1bG1vbmFyeSwgdHlwZT0nZWNkZl9vdmVybGF5JykKCmBgYAoKIyMjIyA4LjIgU3VtbWFyeSBvZiBjaGFuZ2UKClN1bW1hcmlzZSBpbiBwbG90CgpgYGB7cn0KcGxvdF9jb3VudGVyZmFjdHVhbChtb2RlbF9kYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCAlPiUgZmlsdGVyKHllYXI8PTE5NjEpLCBtb2RlbD1tX2V4dHJhcHVsbW9uYXJ5LCAKICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgb3V0Y29tZT1pbmNfMTAwa19leHRyYXB1bG1vbmFyeSwgcmVfZm9ybXVsYSA9IE5BKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCw1MCkpCiAgCmdnc2F2ZShoZXJlKCJmaWd1cmVzL3MxMS50aWZmIiksIHdpZHRoPTEwKQoKYGBgCgpTdW1tYXJpc2UgbnVtZXJpY2FsbHkuCgpgYGB7cn0KCm92ZXJhbGxfY2hhbmdlX2V4dHJhcHVsbW9uYXJ5IDwtIHN1bW1hcmlzZV9jaGFuZ2UobW9kZWxfZGF0YT1vdmVyYWxsX3NjYWZmb2xkICU+JSBmaWx0ZXIoeWVhcjw9MTk2MSksIG1vZGVsPW1fZXh0cmFwdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3I9cG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgZ3JvdXBpbmdfdmFyPU5VTEwsIHJlX2Zvcm11bGEgPSBOQSkKCiN3YW50IHRvIGtlZXAgdGhlIHN1bW1hcnkgZXN0aW1hdGVzIGhlcmUKdG9rZWVwIDwtIGMoInBlYWtfc3VtbWFyeSIsICJsZXZlbF9zdW1tYXJ5IiwgInNsb3BlX3N1bW1hcnkiKQoKI3N1bW1hcnkgbWVhc3VyZXMgaW4gYSB0YWJsZQpvdmVyYWxsX2NoYW5nZV9leHRyYXB1bG1vbmFyeSAlPiUKICBrZWVwKG5hbWVzKC4pICVpbiUgdG9rZWVwKSAlPiUKICBiaW5kX3Jvd3MoKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoZXN0aW1hdGU6LnVwcGVyKSwgbnVtYmVyLCBhY2N1cmFjeT0wLjAxKSkgJT4lCiAgc2VsZWN0KG1lYXN1cmUsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgZGF0YXRhYmxlKCkKCmBgYAoKIyMjIyA4LjMgQ29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwKCk51bWJlcnMgb2YgZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGF2ZXJ0ZWQgb3ZlcmFsbC4KCmBgYHtyfQoKb3ZlcmFsbF9lcF9jb3VudGVyZiA8LSBjYWxjdWxhdGVfY291bnRlcmZhY3R1YWwobW9kZWxfZGF0YT1vdmVyYWxsX3NjYWZmb2xkICU+JSBmaWx0ZXIoeWVhcjw9MTk2MSksIG1vZGVsPW1fZXh0cmFwdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3IgPSBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKQoKb3ZlcmFsbF9lcF9jb3VudGVyZiRjb3VudGVyX3Bvc3QgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciwgZGlmZl9pbmMxMDBrOmRpZmZfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhycl9pbmMxMDBrOnJyX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4wMSkpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgZGF0YXRhYmxlKCkKCmBgYAoKVG90YWwgZXh0cmFwdWxtb25hcnkgVEIgY2FzZXMgYXZlcnRlZCBiZXR3ZWVuIDE5NTggYW5kIDE5NjMKCmBgYHtyfQoKb3ZlcmFsbF9lcF9jb3VudGVyZiRjb3VudGVyX3Bvc3Rfb3ZlcmFsbCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgpNYWtlIGludG8gVGFibGUgMgoKCmBgYHtyfQpiaW5kX3Jvd3MoCm92ZXJhbGxfcHVsbW9uYXJ5X2NvdW50ZXJmJGNvdW50ZXJfcG9zdCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyLCBkaWZmX2luYzEwMGs6ZGlmZl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHJyX2luYzEwMGs6cnJfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjAxKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUobW9kZWwgPSAiUFRCX3dhcmQiKSwKCm92ZXJhbGxfcHVsbW9uYXJ5X2NvdW50ZXJmJGNvdW50ZXJfcG9zdF9vdmVyYWxsICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgbXV0YXRlKG1vZGVsID0gIlBUQl9vdmVyYWxsIiksCgpvdmVyYWxsX2VwX2NvdW50ZXJmJGNvdW50ZXJfcG9zdCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyLCBkaWZmX2luYzEwMGs6ZGlmZl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHJyX2luYzEwMGs6cnJfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjAxKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBtdXRhdGUobW9kZWwgPSAiRVBUQiIpLAoKb3ZlcmFsbF9lcF9jb3VudGVyZiRjb3VudGVyX3Bvc3Rfb3ZlcmFsbCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZShtb2RlbCA9ICJFUFRCIG92ZXJhbGwiKQoKKSAlPiUKICBzZWxlY3QobW9kZWwsIHllYXIsIGRpZmZfaW5jMTAwaywgZGlmZl9pbmMxMDBrLmxvd2VyOnJyX2luYzEwMGsudXBwZXIsIAogICAgICAgICBjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIsCiAgICAgICAgIHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlcikgJT4lCiAgdHJhbnNtdXRlKG1vZGVsPW1vZGVsLCB5ZWFyPXllYXIsCiAgICAgICAgICAgIGRpZmZfY25yID0gZ2x1ZSgie2RpZmZfaW5jMTAwa30gW3tkaWZmX2luYzEwMGsubG93ZXJ9LCB7ZGlmZl9pbmMxMDBrLnVwcGVyfV0iKSwKICAgICAgICAgICAgcnIgPSBnbHVlKCJ7cnJfaW5jMTAwa30gW3tycl9pbmMxMDBrLmxvd2VyfSwge3JyX2luYzEwMGsudXBwZXJ9XSIpLAogICAgICAgICAgICBjYXNlc19hdmVydGVkID0gZ2x1ZSgie2Nhc2VzX2F2ZXJ0ZWR9IFt7Y2FzZXNfYXZlcnRlZC5sb3dlcn0sIHtjYXNlc19hdmVydGVkLnVwcGVyfV0iKSwKICAgICAgICAgICAgcGN0X2NoYW5nZSA9IGdsdWUoIntwY3RfY2hhbmdlfSBbe3BjdF9jaGFuZ2UubG93ZXJ9LCB7cGN0X2NoYW5nZS51cHBlcn1dIikpICU+JQogIHdyaXRlX2NzdihoZXJlKCJmaWd1cmVzL3RhYmxlMi5jc3YiKSkKCgoKYGBgCgoKCgojIyMjIDguNCBXYXJkLWxldmVsIGV4dHJhLXB1bG1vbmFyeSBzdW1tYXJpZXMKCldhcmQtbGV2ZWwgZXh0cmEtcHVsbW9uYXJ5IGVzdGltYXRlcyBpbiBncmFwaGljYWwgZm9ybS4KCmBgYHtyfQoKcGxvdF9jb3VudGVyZmFjdHVhbChtb2RlbF9kYXRhID0gbWRhdGFfZXh0cmFwdWxtb25hcnksIG1vZGVsPW1fZXh0cmFwdWxtb25hcnksIG91dGNvbWUgPSBpbmNfMTAwaywgCiAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIGdyb3VwaW5nX3ZhciA9IHdhcmQscmVfZm9ybXVsYSA9fih5X251bSphY2ZfcGVyaW9kIHwgd2FyZCksIAogICAgICAgICAgICAgICAgICAgIHdhcmQpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz0gYygwLDc1KSkKICAKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczEyLnRpZmYiKSwgd2lkdGg9MTAsIGhlaWdodD0xMikKCmBgYAoKTnVtZXJpY2FsIHN1bW1hcnkuCgpgYGB7cn0KCndhcmRfY2hhbmdlX2V4dHJhcHVsbW9uYXJ5IDwtIHN1bW1hcmlzZV9jaGFuZ2UobW9kZWxfZGF0YSA9IG1kYXRhX2V4dHJhcHVsbW9uYXJ5LCBtb2RlbCA9IG1fZXh0cmFwdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3IgPSBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCBncm91cGluZ192YXI9d2FyZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0gfih5X251bSphY2ZfcGVyaW9kIHwgd2FyZCkpIAoKI3dhbnQgdG8ga2VlcCB0aGUgc3VtbWFyeSBlc3RpbWF0ZXMgaGVyZQp0b2tlZXAgPC0gYygicGVha19zdW1tYXJ5IiwgImxldmVsX3N1bW1hcnkiLCAic2xvcGVfc3VtbWFyeSIpCgojc3VtbWFyeSBtZWFzdXJlcyBpbiBhIHRhYmxlCndhcmRfY2hhbmdlX2V4dHJhcHVsbW9uYXJ5ICAlPiUKICBrZWVwKG5hbWVzKC4pICVpbiUgdG9rZWVwKSAlPiUKICBiaW5kX3Jvd3MoKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoZXN0aW1hdGU6LnVwcGVyKSwgbnVtYmVyLCBhY2N1cmFjeT0wLjAxKSkgJT4lCiAgc2VsZWN0KG1lYXN1cmUsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgoKYGBgCgoKIyMjIDkuIEFnZS1zZXggbW9kZWwKCiMjIyMgOS4xIEZJdCB0aGUgbW9kZWwKCkZpdCB0aGUgbW9kZWwKCihOb3QgcmV3cml0dGVuIHRoZSBmdW5jdGlvbnMgZm9yIHRoaXMgeWV0KQoKYGBge3J9CgptZGF0YV9hZ2Vfc2V4IDwtIGNhc2VzX2J5X2FnZV9zZXggJT4lCiAgZmlsdGVyKHRiX3R5cGU9PSJQdWxtb25hcnkiKSAlPiUKICBtdXRhdGUoYWNmX3BlcmlvZCA9IGNhc2Vfd2hlbih5ZWFyICVpbiUgYygxOTUwOjE5NTYpIH4gImEuIHByZS1hY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTcpIH4gImIuIGFjZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciAlaW4lIGMoMTk1ODoxOTYzKSB+ICJjLiBwb3N0LWFjZiIpKSAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkgJT4lCiAgZ3JvdXBfYnkoYWdlLCBzZXgpICU+JQogIG11dGF0ZSh5X251bSA9IHJvd19udW1iZXIoKSkgJT4lCiAgdW5ncm91cCgpCgptX2FnZV9zZXggPC0gYnJtKAogIGNhc2VzIH4geV9udW0gKyAoYWNmX3BlcmlvZCkqKGFnZSpzZXgpICsgKGFjZl9wZXJpb2Q6eV9udW0pKihhZ2Uqc2V4KSwKICAgICAgICAgICAgICAgICAgZGF0YSA9IG1kYXRhX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9IG5lZ2Jpbm9taWFsKCksCiAgICAgICAgICAgICAgICAgIHNlZWQgPSAxMjM0LAogICAgICAgICAgICAgICAgICB0aHJlYWRzID0gdGhyZWFkaW5nKDIsIGdyYWluc2l6ZSA9IDEwMCwgc3RhdGljID0gVFJVRSksICNmb3IgZXhhY3QgcmVwcm9kdWNpYmlsaXR5CiAgICAgICAgICAgICAgICAgIGJhY2tlbmQgPSAiY21kc3RhbnIiLAogICAgICAgICAgICAgICAgICBjaGFpbnMgPSA0LCBjb3JlcyA9IDQsIAogICAgICAgICAgICAgICAgICBwcmlvciA9IHByaW9yKG5vcm1hbCgwLDEpLCBjbGFzcyA9IEludGVyY2VwdCkgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGdhbW1hKDAuMDEsIDAuMDEpLCBjbGFzcyA9IHNoYXBlKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3Iobm9ybWFsKDAsIDEpLCBjbGFzcyA9IGIpKQoKc3VtbWFyeShtX2FnZV9zZXgpCnBsb3QobV9hZ2Vfc2V4KQpwcF9jaGVjayhtX2FnZV9zZXgsIHR5cGU9J2VjZGZfb3ZlcmxheScpCgpgYGAKClN1bW1hcmlzZSBwb3N0ZXJpb3IKCgpgYGB7cn0KCiNwb3N0ZXJpb3IgZHJhd3MsIGFuZCBzdW1tYXJpc2UKYWdlX3NleF9zdW1tYXJ5IDwtIG1kYXRhX2FnZV9zZXggJT4lCiAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWNmX3BlcmlvZCwgYWdlLCBzZXgpICU+JQogIGFkZF9lcHJlZF9kcmF3cyhtX2FnZV9zZXgpICU+JQogIGdyb3VwX2J5KHllYXIyLCBhY2ZfcGVyaW9kLCBhZ2UsIHNleCkgJT4lCiAgbWVhbl9xaSgpICU+JQogIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKGFjZl9wZXJpb2Q9PSJhLiBwcmUtYWNmIiB+ICJCZWZvcmUgSW50ZXJ2ZW50aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kPT0iYy4gcG9zdC1hY2YiIH4gIlBvc3QgSW50ZXJ2ZW50aW9uIikpCgojY3JlYXRlIHRoZSBjb3VudGVyZmFjdHVhbCAobm8gaW50ZXJ2ZW50aW9uKSwgYW5kIHN1bW1hcmlzZQphZ2Vfc2V4X2NvdW50ZXJmYWN0IDwtIAogIHRpYmJsZSh5ZWFyID0gbWRhdGFfYWdlX3NleCR5ZWFyLAogICAgICAgICB5ZWFyMiA9IG1kYXRhX2FnZV9zZXgkeWVhcjIsCiAgICAgICAgIHlfbnVtID0gbWRhdGFfYWdlX3NleCR5X251bSwKICAgICAgICAgYWdlID0gbWRhdGFfYWdlX3NleCRhZ2UsCiAgICAgICAgIHNleCA9IG1kYXRhX2FnZV9zZXgkc2V4LAogICAgICAgICBhY2ZfcGVyaW9kID0gZmFjdG9yKCJhLiBwcmUtYWNmIikpICU+JQogIGFkZF9lcHJlZF9kcmF3cyhtX2FnZV9zZXgpICU+JQogIGdyb3VwX2J5KHllYXIyLCBhY2ZfcGVyaW9kLCBhZ2UsIHNleCkgJT4lCiAgbWVhbl9xaSgpICU+JQogIG11dGF0ZShhY2ZfcGVyaW9kID0gY2FzZV93aGVuKGFjZl9wZXJpb2Q9PSJhLiBwcmUtYWNmIiB+ICJCZWZvcmUgSW50ZXJ2ZW50aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kPT0iYy4gcG9zdC1hY2YiIH4gIlBvc3QgSW50ZXJ2ZW50aW9uIikpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZT09IjAwXzA1IiB+ICIwIHRvIDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjA2XzE1IiB+ICIwNiB0byAxNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMTZfMjUiIH4gIjE2IHRvIDI1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIyNl8zNSIgfiAiMjYgdG8gMzV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjM2XzQ1IiB+ICIzNiB0byA0NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNDZfNTUiIH4gIjQ2IHRvIDU1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI1Nl82NSIgfiAiNTYgdG8gNjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjY1KyIgfiAiNjUgJiB1cCB5IikpICU+JQogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0gIk0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0gIkYiIH4gIkZlbWFsZSIpKSAKCgoKYWdlX3NleF9zdW1tYXJ5ICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZT09IjAwXzA1IiB+ICIwIHRvIDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjA2XzE1IiB+ICIwNiB0byAxNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMTZfMjUiIH4gIjE2IHRvIDI1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIyNl8zNSIgfiAiMjYgdG8gMzV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjM2XzQ1IiB+ICIzNiB0byA0NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNDZfNTUiIH4gIjQ2IHRvIDU1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI1Nl82NSIgfiAiNTYgdG8gNjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjY1KyIgfiAiNjUgJiB1cCB5IikpICU+JQogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0gIk0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0gIkYiIH4gIkZlbWFsZSIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9yaWJib24oYWVzKHltaW49LmVwcmVkLmxvd2VyLCB5bWF4PS5lcHJlZC51cHBlciwgeD15ZWFyMiwgZ3JvdXAgPSBhY2ZfcGVyaW9kLCBmaWxsPWFjZl9wZXJpb2QpLCBhbHBoYT0wLjUpICsKICBnZW9tX3JpYmJvbihkYXRhID0gYWdlX3NleF9jb3VudGVyZmFjdCAlPiUgZmlsdGVyKHllYXI+PTE5NTYpLCAKICAgICAgICAgICAgICBhZXMoeW1pbj0uZXByZWQubG93ZXIsIHltYXg9LmVwcmVkLnVwcGVyLCB4PXllYXIyLCBmaWxsPSJDb3VudGVyZmFjdHVhbCIpLCBhbHBoYT0wLjUpICsKICBnZW9tX2xpbmUoZGF0YSA9IGFnZV9zZXhfY291bnRlcmZhY3QgJT4lIGZpbHRlcih5ZWFyPj0xOTU2KSwgCiAgICAgICAgICAgICAgYWVzKHk9LmVwcmVkLCB4PXllYXIyLCBjb2xvdXI9IkNvdW50ZXJmYWN0dWFsIikpICsKICBnZW9tX2xpbmUoYWVzKHk9LmVwcmVkLCB4PXllYXIyLCBncm91cD1hY2ZfcGVyaW9kLCAgY29sb3VyPWFjZl9wZXJpb2QpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKGFnZSA9IGNhc2Vfd2hlbihhZ2U9PSIwMF8wNSIgfiAiMCB0byA1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIwNl8xNSIgfiAiMDYgdG8gMTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjE2XzI1IiB+ICIxNiB0byAyNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMjZfMzUiIH4gIjI2IHRvIDM1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIzNl80NSIgfiAiMzYgdG8gNDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjQ2XzU1IiB+ICI0NiB0byA1NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNTZfNjUiIH4gIjU2IHRvIDY1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI2NSsiIH4gIjY1ICYgdXAgeSIpKSAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09ICJNIiB+ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNleD09ICJGIiB+ICJGZW1hbGUiKSkgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIkJlZm9yZSBJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJiLiBhY2YiIH4gIkNvdW50ZXJmYWN0dWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSklPiUKICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZDIgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09IkJlZm9yZSBJbnRlcnZlbnRpb24iIH4gIlByZS1BQ0YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJDb3VudGVyZmFjdHVhbCIgfiAiQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kPT0iUG9zdCBJbnRlcnZlbnRpb24iIH4gIlBvc3QtQUNGIikpLCAKICBhZXMoeT1jYXNlcywgeD15ZWFyMiwgc2hhcGU9ZmN0X3JlbGV2ZWwoYWNmX3BlcmlvZDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHJlLUFDRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQUNGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQb3N0LUFDRiIpKSwgc2l6ZT0yKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIGZhY2V0X2dyaWQoZmN0X3JlbGV2ZWwoYWdlLCAiNjUgJiB1cCB5IiwgIjU2IHRvIDY1eSIsICI0NiB0byA1NXkiLCAiMzYgdG8gNDV5IiwgIjI2IHRvIDM1eSIsICIxNiB0byAyNXkiLCAiMDYgdG8gMTV5IiwgIjAgdG8gNXkiKX5zZXgsIAogICAgICAgICAgICAgc2NhbGVzID0gImZyZWVfeSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfYXhpcyhhbmdsZSA9IDkwKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSAsIG5hbWU9Ik1vZGVsIGVzdGltYXRlczoiKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjREUwRDkyIiwgImdyZXk1MCIsICIjNEQ2Q0ZBIikgLCBuYW1lPSJNb2RlbCBlc3RpbWF0ZXM6IikgKwogIHNjYWxlX3NoYXBlX2Rpc2NyZXRlKG5hbWU9IkVtcHJpY2FsIGRhdGEgKHBlcmlvZCk6IiwgbmEudHJhbnNsYXRlID0gRikgKwogIGxhYnMoCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJDYXNlIG5vdGlmaWNhdGlvbnMgKG4pIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsICAgICAgICAgIAogICAgICAgIGxlZ2VuZC5ib3g9InZlcnRpY2FsIiwgCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKICAKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczE0LnRpZmYiKSwgaGVpZ2h0PTEwKQoKYGBgCgojIyMjIDkuMiBTdW1tYXJ5IG9mIGltcGFjdCBvZiBpbnRlcnZlbnRpb24KCkNhbGN1bGF0ZSBzdW1tYXJ5IGVmZmVjdHMKCmBgYHtyfQoKI1BlYWsKb3V0X2FnZV9zZXhfMSA8LSBjcm9zc2luZyhtZGF0YV9hZ2Vfc2V4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDgpLAogICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYi4gYWNmIikpCgpwZWFrX2RyYXdzX2FnZV9zZXggPC0gYWRkX2VwcmVkX2RyYXdzKG5ld2RhdGEgPSBvdXRfYWdlX3NleF8xLAogICAgICAgICAgICAgICAgICBvYmplY3QgPSBtX2FnZV9zZXgpICU+JQogICAgZ3JvdXBfYnkoLmRyYXcsIGFnZSwgc2V4KSAlPiUKICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3QoLmVwcmVkKS9maXJzdCguZXByZWQpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnBlYWsiKQogIApwZWFrX3N1bW1hcnlfYWdlX3NleCA8LSBwZWFrX2RyYXdzX2FnZV9zZXggJT4lCiAgICBncm91cF9ieShhZ2UsIHNleCkgJT4lCiAgICBtZWFuX3FpKGVzdGltYXRlKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnBlYWsiKQoKCiNMZXZlbAogCm91dF9hZ2Vfc2V4XzIgPC0gY3Jvc3NpbmcobWRhdGFfYWdlX3NleCAlPiUgCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoeV9udW0sIGFnZSwgc2V4KSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih5X251bSA9PSA5KSwKICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikpCiAgCmxldmVsX2RyYXdzX2FnZV9zZXggPC0gYWRkX2VwcmVkX2RyYXdzKG5ld2RhdGEgPSBvdXRfYWdlX3NleF8yLAogICAgICAgICAgICAgICAgICBvYmplY3QgPSBtX2FnZV9zZXgpICU+JQogICAgYXJyYW5nZSh5X251bSwgLmRyYXcpICU+JQogICAgZ3JvdXBfYnkoLmRyYXcsIGFnZSwgc2V4KSAlPiUKICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3QoLmVwcmVkKS9maXJzdCguZXByZWQpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLmxldmVsIikKICAKbGV2ZWxfc3VtbWFyeV9hZ2Vfc2V4IDwtIGxldmVsX2RyYXdzX2FnZV9zZXggJT4lCiAgICBncm91cF9ieShhZ2UsIHNleCkgJT4lCiAgICBtZWFuX3FpKGVzdGltYXRlKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLmxldmVsIikKCiNTbG9wZQoKb3V0X2FnZV9zZXhfMyA8LSBjcm9zc2luZyhtZGF0YV9hZ2Vfc2V4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtICVpbiUgYyg5LDE0KSksCiAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYy4gcG9zdC1hY2YiKSkKICAKc2xvcGVfZHJhd3NfYWdlX3NleCA8LSBhZGRfZXByZWRfZHJhd3MobmV3ZGF0YSA9IG91dF9hZ2Vfc2V4XzMsCiAgICAgICAgICAgICAgICAgIG9iamVjdCA9IG1fYWdlX3NleCkgJT4lCiAgICAgICAgYXJyYW5nZSh5X251bSkgJT4lCiAgICAgICAgdW5ncm91cCgpICU+JQogICAgICAgIGdyb3VwX2J5KC5kcmF3LCB5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgIHN1bW1hcmlzZShzbG9wZSA9IGxhc3QoLmVwcmVkKS9maXJzdCguZXByZWQpKSAlPiUKICAgICAgICB1bmdyb3VwKCkgJT4lCiAgICAgICAgZ3JvdXBfYnkoLmRyYXcsIGFnZSwgc2V4KSAlPiUKICAgICAgICBzdW1tYXJpc2UoZXN0aW1hdGUgPSBsYXN0KHNsb3BlKS9maXJzdChzbG9wZSkpICU+JQogICAgICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnNsb3BlIikKICAKc2xvcGVfc3VtbWFyeV9hZ2Vfc2V4IDwtIHNsb3BlX2RyYXdzX2FnZV9zZXggJT4lCiAgICAgZ3JvdXBfYnkoYWdlLCBzZXgpICU+JQogICAgICBtZWRpYW5fcWkoZXN0aW1hdGUpICU+JQogICAgICBtdXRhdGUobWVhc3VyZSA9ICJSUi5zbG9wZSIpCgpgYGAKCgpOdW1lcmljYWwgc3VtbWFyeSBvZiB0aGVzZSBzdW1tYXJ5IHJlc3VsdHMKCmBgYHtyfQoKYmluZF9yb3dzKAogIHBlYWtfc3VtbWFyeV9hZ2Vfc2V4LCBsZXZlbF9zdW1tYXJ5X2FnZV9zZXgsIHNsb3BlX3N1bW1hcnlfYWdlX3NleAopICU+JQogIG11dGF0ZShhY3Jvc3MoYyhlc3RpbWF0ZToudXBwZXIpLCBudW1iZXIsIGFjY3VyYWN5PTAuMDEpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBkYXRhdGFibGUoKQoKCgpgYGAKCkFzIGEgZmlndXJlCgpgYGB7cn0KCnBlYWtfZ19hZ2Vfc2V4IDwtIHBlYWtfc3VtbWFyeV9hZ2Vfc2V4ICU+JQogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0iTSIgfiAiTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBzZXg9PSJGIiB+ICJGZW1hbGUiKSkgJT4lCiAgbXV0YXRlKGFnZSA9IGNhc2Vfd2hlbihhZ2U9PSIwMF8wNSIgfiAiMCB0byA1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIwNl8xNSIgfiAiMDYgdG8gMTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjE2XzI1IiB+ICIxNiB0byAyNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMjZfMzUiIH4gIjI2IHRvIDM1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIzNl80NSIgfiAiMzYgdG8gNDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjQ2XzU1IiB+ICI0NiB0byA1NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNTZfNjUiIH4gIjU2IHRvIDY1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI2NSsiIH4gIjY1ICYgdXAgeSIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD0xKSwgbGluZXR5cGU9MikrCiAgZ2VvbV9wb2ludHJhbmdlKGFlcyh4PWFnZSwgeT1lc3RpbWF0ZSwgeW1pbj0ubG93ZXIsIHltYXg9LnVwcGVyLCBncm91cD1zZXgsIGNvbG91cj1zZXgsIHNoYXBlPXNleCksCiAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjUpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjQ0Q3QUM1IiwgImNhZGV0Ymx1ZTMiKSwgbmFtZT0iIikgKwogIHNjYWxlX3NoYXBlKG5hbWU9IiIpICsKICBsYWJzKHg9IiIsCiAgICAgICB5PSJSZWxhdGl2ZSByYXRlICg5NSUgVUkpIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKQoKI2xldmVsIHBsb3QKbGV2ZWxfZ19hZ2Vfc2V4IDwtIGxldmVsX3N1bW1hcnlfYWdlX3NleCAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Ik0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iRiIgfiAiRmVtYWxlIikpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9MSksIGxpbmV0eXBlPTIpKwogIGdlb21fcG9pbnRyYW5nZShhZXMoeD1hZ2UsIHk9ZXN0aW1hdGUsIHltaW49Lmxvd2VyLCB5bWF4PS51cHBlciwgZ3JvdXA9c2V4LCBjb2xvdXI9c2V4LCBzaGFwZT1zZXgpLAogICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC41KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiI0NEN0FDNSIsICJjYWRldGJsdWUzIiksIG5hbWU9IiIpICsKICBzY2FsZV9zaGFwZShuYW1lPSIiKSArCiAgbGFicyh4PSIiLAogICAgICAgeT0iUmVsYXRpdmUgcmF0ZSAoOTUlIFVJKSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKCiNzbG9wZSBwbG90CnNsb3BlX2dfYWdlX3NleCA8LSBzbG9wZV9zdW1tYXJ5X2FnZV9zZXggJT4lCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbihzZXg9PSJNIiB+ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNleD09IkYiIH4gIkZlbWFsZSIpKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZT09IjAwXzA1IiB+ICIwIHRvIDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjA2XzE1IiB+ICIwNiB0byAxNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMTZfMjUiIH4gIjE2IHRvIDI1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIyNl8zNSIgfiAiMjYgdG8gMzV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjM2XzQ1IiB+ICIzNiB0byA0NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNDZfNTUiIH4gIjQ2IHRvIDU1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI1Nl82NSIgfiAiNTYgdG8gNjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjY1KyIgfiAiNjUgJiB1cCB5IikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PTEpLCBsaW5ldHlwZT0yKSsKICBnZW9tX3BvaW50cmFuZ2UoYWVzKHg9YWdlLCB5PWVzdGltYXRlLCB5bWluPS5sb3dlciwgeW1heD0udXBwZXIsIGdyb3VwPXNleCwgY29sb3VyPXNleCwgc2hhcGU9c2V4KSwKICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNDRDdBQzUiLCAiY2FkZXRibHVlMyIpLCBuYW1lPSIiKSArCiAgc2NhbGVfc2hhcGUobmFtZT0iIikgKwogIGxhYnMoeD0iIiwKICAgICAgIHk9IlJlbGF0aXZlIHJhdGUgKDk1JSBVSSkiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgoKYGBgCgoKIyMjIyA5LjMgQ29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwKCmBgYHtyfQoKY291bnRlcmZhY3RfYWdlX3NleCA8LQogICAgICBhZGRfZXByZWRfZHJhd3Mob2JqZWN0ID0gbV9hZ2Vfc2V4LAogICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IG1kYXRhX2FnZV9zZXggJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5ZWFyLCB5ZWFyMiwgeV9udW0sIGFnZSwgc2V4KSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGFjZl9wZXJpb2QgPSAiYS4gcHJlLWFjZiIpKSAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHNlbGVjdCh5ZWFyLCBhZ2UsIHNleCwgLmRyYXcsIC5lcHJlZF9jb3VudGVyZiA9IC5lcHJlZCkKICAKI0NhbGN1YXRlIHByZWRpY3RlZCBudW1iZXIgb2YgY2FzZXMgcGVyIGRyYXcsIHRoZW4gc3VtbWFyaXNlLgpwb3N0X2NoYW5nZV9hZ2Vfc2V4IDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWdlLCBzZXgsIGFjZl9wZXJpb2QpKSAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgc2VsZWN0KHllYXIsIGFnZSwgc2V4LCAuZHJhdywgLmVwcmVkKSAKICAKI2ZvciB0aGUgb3ZlcmFsbCBwZXJpb2QKY291bnRlcmZhY3Rfb3ZlcmFsbF9hZ2Vfc2V4IDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIikpICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgc2VsZWN0KGFnZSwgc2V4LCAuZHJhdywgLmVwcmVkKSAgJT4lCiAgICAgIGdyb3VwX2J5KGFnZSwgc2V4LCAuZHJhdykgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWRfY291bnRlcmYgPSBzdW0oLmVwcmVkKSkgJT4lCiAgICAgIG11dGF0ZSh5ZWFyID0gIk92ZXJhbGwgKDE5NTgtMTk2MykiKQogIAojQ2FsY3VhdGUgaW5jaWRlbmNlIHBlciBkcmF3LCB0aGVuIHN1bW1hcmlzZS4KcG9zdF9jaGFuZ2Vfb3ZlcmFsbF9hZ2Vfc2V4IDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWdlLCBzZXgsIGFjZl9wZXJpb2QpKSAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHNlbGVjdChhZ2UsIHNleCwgLmRyYXcsIC5lcHJlZCkgJT4lCiAgICAgIGdyb3VwX2J5KC5kcmF3LCBhZ2UsIHNleCkgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWQgPSBzdW0oLmVwcmVkKSkgCiAgCmNvdW50ZXJfcG9zdF9vdmVyYWxsX2FnZV9zZXggPC0KICBsZWZ0X2pvaW4oY291bnRlcmZhY3Rfb3ZlcmFsbF9hZ2Vfc2V4LCBwb3N0X2NoYW5nZV9vdmVyYWxsX2FnZV9zZXgpICU+JQogICAgbXV0YXRlKGNhc2VzX2F2ZXJ0ZWQgPSAuZXByZWRfY291bnRlcmYtLmVwcmVkLAogICAgICAgICAgIHBjdF9jaGFuZ2UgPSAoLmVwcmVkIC0gLmVwcmVkX2NvdW50ZXJmKS8uZXByZWRfY291bnRlcmYpICU+JQogICAgZ3JvdXBfYnkoYWdlLCBzZXgpICU+JQogICAgbWVhbl9xaShjYXNlc19hdmVydGVkLCBwY3RfY2hhbmdlKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZSh5ZWFyID0gIk92ZXJhbGwgKDE5NTgtMTk2MykiKSAKCgphZ2Vfc2V4X3R4dCA8LSBjb3VudGVyX3Bvc3Rfb3ZlcmFsbF9hZ2Vfc2V4ICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgdHJhbnNtdXRlKHllYXIgPSBhcy5jaGFyYWN0ZXIoeWVhciksCiAgICAgICAgICAgIHNleCA9IHNleCwKICAgICAgICAgICAgYWdlID0gYWdlLAogICAgICAgICAgICBjYXNlc19hdmVydGVkID0gZ2x1ZTo6Z2x1ZSgie2Nhc2VzX2F2ZXJ0ZWR9XG4oe2Nhc2VzX2F2ZXJ0ZWQubG93ZXJ9IHRvIHtjYXNlc19hdmVydGVkLnVwcGVyfSkiKSwKICAgICAgICAgICAgcGN0X2NoYW5nZSA9IGdsdWU6OmdsdWUoIntwY3RfY2hhbmdlfVxuKHtwY3RfY2hhbmdlLmxvd2VyfSB0byB7cGN0X2NoYW5nZS51cHBlcn0pIikpCgoKYWdlX3NleF90eHQgJT4lIGRhdGF0YWJsZSgpCgoKYGBgCgpgYGB7cn0KCmNvdW50ZXJmYWN0dWFsX2dfYWdlX3NleCA8LSBjb3VudGVyX3Bvc3Rfb3ZlcmFsbF9hZ2Vfc2V4ICU+JSAKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Ik0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iRiIgfiAiRmVtYWxlIikpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnRyYW5nZShhZXMoeCA9IGFnZSwgeT1jYXNlc19hdmVydGVkLCB5bWluPWNhc2VzX2F2ZXJ0ZWQubG93ZXIsIHltYXg9Y2FzZXNfYXZlcnRlZC51cHBlciwgY29sb3VyPXNleCwgc2hhcGU9c2V4KSwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9MC41KSkgKyAKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNDRDdBQzUiLCAiY2FkZXRibHVlMyIpLCBuYW1lPSIiKSArCiAgc2NhbGVfc2hhcGUobmFtZT0iIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGxhYnMoeD0iIiwKICAgICAgIHk9Ik51bWJlciAoOTUlIFVJKSIsCiAgICAgICBjb2xvdXI9IiIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmNvdW50ZXJmYWN0dWFsX2dfYWdlX3NleApgYGAKCkpvaW4gdG9nZXRoZXIgZm9yIEZpZ3VyZSAzLgoKCmBgYHtyfQoKKHBlYWtfZ19hZ2Vfc2V4ICsgbGV2ZWxfZ19hZ2Vfc2V4KSAvIChzbG9wZV9nX2FnZV9zZXggKyBjb3VudGVyZmFjdHVhbF9nX2FnZV9zZXgpICsgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAiQSIpICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSAmIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvZjMudGlmZiIpLCB3aWR0aCA9IDEyLCBoZWlnaHQ9OCkKCgpgYGAKCgo=